Compare commits
64 Commits
f68b23fcf1
...
detached
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f28401e7f | ||
|
|
e4db999ad9 | ||
|
|
3b4b5d3a58 | ||
|
|
eae69f7c8a | ||
|
|
a54e7d1823 | ||
|
|
aaa3a655bb | ||
|
|
b979f8ff42 | ||
|
|
51458e2649 | ||
|
|
c58f020e9e | ||
|
|
e22048fe0d | ||
|
|
649858761e | ||
|
|
275b1319f8 | ||
|
|
cfcf8f3136 | ||
|
|
afb26ced5d | ||
|
|
2f9d589b33 | ||
|
|
0a7f206ad2 | ||
|
|
c929b94031 | ||
|
|
cda8612eb7 | ||
|
|
a52d9270d9 | ||
|
|
bb6341763a | ||
|
|
c19ae00e84 | ||
|
|
82c71d2d90 | ||
|
|
a9938ba354 | ||
|
|
938cb3eb96 | ||
|
|
0b7ce2758b | ||
|
|
ef6dbabedf | ||
|
|
9f9ec214f8 | ||
|
|
3ba87c1648 | ||
|
|
79e0c88bd3 | ||
|
|
df296fb66f | ||
|
|
46e10dd62b | ||
|
|
c81dbed42c | ||
|
|
57e1466d59 | ||
|
|
b2e481bfa6 | ||
|
|
6928521173 | ||
|
|
a04bc121c9 | ||
|
|
f0d0614873 | ||
|
|
d4e55b4bff | ||
|
|
995ef6122b | ||
|
|
fdc43477d0 | ||
|
|
f0ad547aeb | ||
|
|
7eeef8fb20 | ||
|
|
a0ea166d2b | ||
|
|
fd151f7a80 | ||
|
|
aef0e89631 | ||
|
|
9785c46fec | ||
|
|
d1e0361dec | ||
|
|
187e97b36b | ||
|
|
757b7033fe | ||
|
|
fd63961e3c | ||
|
|
dbfd56d002 | ||
|
|
d07d3b6a1d | ||
|
|
d7f64050b2 | ||
|
|
7c9ede446f | ||
|
|
ee9e7c29a8 | ||
|
|
a7c6b4e1fc | ||
|
|
fdcec74585 | ||
|
|
784a2a3af3 | ||
|
|
6af2458241 | ||
|
|
40683f4bab | ||
|
|
d81b9eced0 | ||
|
|
488e8de86a | ||
|
|
71c025b627 | ||
|
|
a938495d40 |
30
1111
30
1111
@@ -14,10 +14,6 @@ api交易
|
||||
yto0mg6r05kkyolwefqw
|
||||
db2e87093c120766bca60d2282y72760688
|
||||
|
||||
api交易
|
||||
yto0mg6r05kkyolwefqw
|
||||
db2e87093c120766bca60d2282y72760688
|
||||
|
||||
yx20250715@gmail.com
|
||||
Abc12345678
|
||||
|
||||
@@ -25,23 +21,27 @@ Abc12345678
|
||||
248.23
|
||||
2611
|
||||
|
||||
bitmart\框架.py 读取这个代码文件夹
|
||||
|
||||
|
||||
基础开仓逻辑
|
||||
基于前一根有效 K 线(实体 ≥ 0.1):
|
||||
做多触发价 = 当前 k 线开盘价 + 实体 / 5(收盘价向上 1/5 实体)
|
||||
做空触发价 = 当前 k 线开盘价 - 实体 / 5(收盘价向下 1/5 实体)
|
||||
|
||||
若已有持仓,在 3 分钟 K 线的第一分钟可单独检测反手:
|
||||
持空反手做多:价格涨到 开仓价 + 前一根实体 / 5
|
||||
持多反手做空:价格跌到 开仓价 - 前一根实体 / 5
|
||||
|
||||
|
||||
|
||||
|
||||
做多触发价 = 当前 k 线开盘价 + 上一根实体 / 4
|
||||
做空触发价 = 当前 k 线开盘价 - 上一根实体 / 4
|
||||
|
||||
反手信号:
|
||||
持空反手做多:价格涨到 当前k线开仓价 + 前一根实体 / 3
|
||||
持多反手做空:价格跌到 当前k线开仓价 - 前一根实体 / 3
|
||||
持多反手做空:价格跌到 k 线的上阴线涨幅大于 0.01%,当前这个根跌倒了上一根的k线的实体边,平仓做空
|
||||
持空反手做多:价格涨到 k 线的下阴线跌幅大于 0.01%,当前这个根涨到了上一根的k线的实体边,平仓做多
|
||||
|
||||
上一根阴线 + 当前阳线(做多形态)→ 不按上三分之一做空。
|
||||
上一根阳线 + 当前阴线(做空形态)→ 不按下三分之一做多。
|
||||
|
||||
|
||||
|
||||
反手做空,当前 k 线开盘价 - 上一根实体 / 3(收盘价向下 1/3 实体)
|
||||
反手做多,当前 k 线开盘价 + 上一根实体 / 3(收盘价向上 1/3 实体)
|
||||
|
||||
|
||||
2574
|
||||
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
# 自适应三分位趋势策略
|
||||
|
||||
优化版实盘策略:趋势过滤 + 动态阈值 + 信号确认 + 完整风险管理。
|
||||
|
||||
## 目录结构
|
||||
|
||||
- `config.py` - 策略参数(趋势模式、止损止盈、确认条件等)
|
||||
- `indicators.py` - EMA、ATR 指标
|
||||
- `data_fetcher.py` - **Bitmart 方式**拉取 5/15/60 分钟 K 线(API 或 CSV)
|
||||
- `strategy_core.py` - 趋势判断、有效 K 线、动态触发价、信号确认
|
||||
- `backtest.py` - 回测引擎(硬止损、止盈、移动止损、时间止损)
|
||||
- `trade.py` - 实盘交易入口(拉数据 + 信号,开平仓需对接现有 Bitmart 下单逻辑)
|
||||
- `data/` - 回测用 K 线 CSV(可选,无则 API 拉取)
|
||||
|
||||
## 回测(先出数据再回测)
|
||||
|
||||
在**项目根目录** `lm_code` 下执行:
|
||||
|
||||
```bash
|
||||
# 使用 API 拉取数据并回测(会请求 Bitmart 接口)
|
||||
python adaptive_third_strategy/backtest.py --start 2025-01-01 --end 2025-01-30 --capital 10000
|
||||
|
||||
# 仅用本地已有 CSV 回测(需先抓过数据)
|
||||
python adaptive_third_strategy/backtest.py --start 2025-01-01 --end 2025-01-30 --no-api
|
||||
```
|
||||
|
||||
数据会保存到 `adaptive_third_strategy/data/kline_5m.csv`、`kline_15m.csv`、`kline_60m.csv`。
|
||||
回测结果输出到控制台,交易明细保存到 `adaptive_third_strategy/backtest_trades.csv`。
|
||||
|
||||
## 仅抓取数据(Bitmart 方式)
|
||||
|
||||
与 `bitmart/回测.py`、`bitmart/抓取多周期K线.py` 相同的 API 调用方式:
|
||||
|
||||
```bash
|
||||
cd adaptive_third_strategy && python -c "
|
||||
from data_fetcher import fetch_multi_timeframe, save_klines_csv
|
||||
import time, os
|
||||
end_ts = int(time.time())
|
||||
start_ts = end_ts - 30*24*3600
|
||||
data = fetch_multi_timeframe(start_ts, end_ts, [5, 15, 60])
|
||||
os.makedirs('data', exist_ok=True)
|
||||
for step, klines in data.items():
|
||||
save_klines_csv(klines, f'data/kline_{step}m.csv')
|
||||
"
|
||||
```
|
||||
|
||||
需在项目根目录 `lm_code` 下执行时,先 `import sys; sys.path.insert(0, '..')` 或使用:
|
||||
|
||||
```bash
|
||||
python -c "
|
||||
import sys, os
|
||||
sys.path.insert(0, os.getcwd())
|
||||
from adaptive_third_strategy.data_fetcher import fetch_multi_timeframe, save_klines_csv
|
||||
import time
|
||||
end_ts = int(time.time())
|
||||
start_ts = end_ts - 30*24*3600
|
||||
data = fetch_multi_timeframe(start_ts, end_ts, [5, 15, 60])
|
||||
os.makedirs('adaptive_third_strategy/data', exist_ok=True)
|
||||
for step, klines in data.items():
|
||||
save_klines_csv(klines, f'adaptive_third_strategy/data/kline_{step}m.csv')
|
||||
"
|
||||
```
|
||||
|
||||
## 实盘
|
||||
|
||||
```bash
|
||||
python adaptive_third_strategy/trade.py
|
||||
```
|
||||
|
||||
当前 `trade.py` 只做:拉 5/15/60 数据、算信号、打日志/钉钉。实际开平仓需在 `run_loop` 里接入你现有的 Bitmart 下单方式(如 `交易/bitmart-三分之一策略交易.py` 的浏览器点击或 API 下单)。
|
||||
|
||||
## 策略要点
|
||||
|
||||
- **数据与周期**:主周期 5 分钟,趋势用 15 分钟 / 1 小时 EMA。
|
||||
- **有效 K 线**:实体 ≥ max(ATR×0.1, 价格×0.05%)。
|
||||
- **趋势过滤**:长期 1h EMA50/200,中期 15m EMA20/50,短期 5m 收盘 vs EMA9;保守模式要求中期+短期一致。
|
||||
- **动态触发**:波动率系数 clamp(实体/ATR, 0.3, 3.0),顺势方向更易触发。
|
||||
- **信号确认**:收盘价、成交量、动量等至少满足 2 项。
|
||||
- **风控**:硬止损 ATR×2、时间止损 3 根 K、移动止损(盈利 1×ATR 后 0.5×ATR 跟踪)、止盈目标 1.5/3/5×ATR。
|
||||
@@ -1,2 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""自适应三分位趋势策略"""
|
||||
@@ -1,464 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
自适应三分位趋势策略 - 回测引擎
|
||||
使用 Bitmart 方式获取数据(API 或 CSV),含硬止损、分批止盈、移动止损、时间止损
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import csv
|
||||
import datetime
|
||||
from typing import List, Dict, Optional, Tuple
|
||||
|
||||
# 使用 UTC 时区,避免 utcfromtimestamp 弃用警告
|
||||
def _utc_dt(ts):
|
||||
if hasattr(datetime, "timezone") and hasattr(datetime.timezone, "utc"):
|
||||
return datetime.datetime.fromtimestamp(ts, tz=datetime.timezone.utc)
|
||||
return datetime.datetime.utcfromtimestamp(ts)
|
||||
|
||||
try:
|
||||
from loguru import logger
|
||||
except ImportError:
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
if ROOT_DIR not in sys.path:
|
||||
sys.path.insert(0, ROOT_DIR)
|
||||
|
||||
from adaptive_third_strategy.config import (
|
||||
STEP_5M,
|
||||
STEP_15M,
|
||||
STEP_60M,
|
||||
ATR_PERIOD,
|
||||
EMA_SHORT,
|
||||
EMA_MID_FAST,
|
||||
EMA_MID_SLOW,
|
||||
EMA_LONG_FAST,
|
||||
EMA_LONG_SLOW,
|
||||
STOP_LOSS_ATR_MULT,
|
||||
TIME_STOP_BARS,
|
||||
TRAIL_START_ATR,
|
||||
TRAIL_ATR_MULT,
|
||||
TP1_ATR,
|
||||
TP2_ATR,
|
||||
TP3_ATR,
|
||||
TP1_RATIO,
|
||||
TP2_RATIO,
|
||||
TP3_RATIO,
|
||||
MIN_BARS_SINCE_ENTRY,
|
||||
SAME_KLINE_NO_REVERSE,
|
||||
REVERSE_BREAK_MULT,
|
||||
REVERSE_LOSS_ATR,
|
||||
MAX_POSITION_PERCENT,
|
||||
BASE_POSITION_PERCENT,
|
||||
CONTRACT_SIZE,
|
||||
FEE_RATE,
|
||||
FEE_FIXED,
|
||||
FEE_FIXED_BACKTEST,
|
||||
MIN_BARS_BETWEEN_TRADES,
|
||||
SLIPPAGE_POINTS,
|
||||
)
|
||||
from adaptive_third_strategy.indicators import get_ema_atr_from_klines, align_higher_tf_ema
|
||||
from adaptive_third_strategy.strategy_core import (
|
||||
check_trigger,
|
||||
get_body_size,
|
||||
build_volume_ma,
|
||||
)
|
||||
from adaptive_third_strategy.data_fetcher import (
|
||||
fetch_multi_timeframe,
|
||||
load_klines_csv,
|
||||
save_klines_csv,
|
||||
)
|
||||
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def ensure_data(
|
||||
start_time: int,
|
||||
end_time: int,
|
||||
data_dir: str,
|
||||
use_api: bool = True,
|
||||
api_key: str = "",
|
||||
secret_key: str = "",
|
||||
) -> Tuple[List[Dict], List[Dict], List[Dict]]:
|
||||
"""
|
||||
确保有 5/15/60 分钟数据。若 data_dir 下已有 CSV 则加载,否则用 API 拉取并保存。
|
||||
use_api=False 时仅从 CSV 加载(需事先抓取)。
|
||||
"""
|
||||
data_dir = data_dir or os.path.join(SCRIPT_DIR, "data")
|
||||
os.makedirs(data_dir, exist_ok=True)
|
||||
paths = {
|
||||
5: os.path.join(data_dir, "kline_5m.csv"),
|
||||
15: os.path.join(data_dir, "kline_15m.csv"),
|
||||
60: os.path.join(data_dir, "kline_60m.csv"),
|
||||
}
|
||||
k5 = load_klines_csv(paths[5])
|
||||
k15 = load_klines_csv(paths[15])
|
||||
k60 = load_klines_csv(paths[60])
|
||||
need_fetch = not k5 or not k15 or not k60
|
||||
if need_fetch and use_api:
|
||||
from adaptive_third_strategy.data_fetcher import DEFAULT_API_KEY, DEFAULT_SECRET_KEY, DEFAULT_MEMO
|
||||
key = api_key or DEFAULT_API_KEY
|
||||
sec = secret_key or DEFAULT_SECRET_KEY
|
||||
data = fetch_multi_timeframe(start_time, end_time, [5, 15, 60], key, sec, DEFAULT_MEMO)
|
||||
k5, k15, k60 = data[5], data[15], data[60]
|
||||
for step, kl in [(5, k5), (15, k15), (60, k60)]:
|
||||
save_klines_csv(kl, paths[step])
|
||||
# 按时间范围过滤(end_time 若为次日 0 点则用 < 以只含 end 日及之前)
|
||||
k5 = [x for x in k5 if start_time <= x["id"] < end_time]
|
||||
k15 = [x for x in k15 if start_time <= x["id"] < end_time]
|
||||
k60 = [x for x in k60 if start_time <= x["id"] < end_time]
|
||||
k5.sort(key=lambda x: x["id"])
|
||||
k15.sort(key=lambda x: x["id"])
|
||||
k60.sort(key=lambda x: x["id"])
|
||||
return k5, k15, k60
|
||||
|
||||
|
||||
def run_backtest(
|
||||
klines_5m: List[Dict],
|
||||
klines_15m: List[Dict],
|
||||
klines_60m: List[Dict],
|
||||
initial_capital: float = 10000.0,
|
||||
use_stop_loss: bool = True,
|
||||
use_take_profit: bool = True,
|
||||
use_trailing_stop: bool = True,
|
||||
use_time_stop: bool = True,
|
||||
fixed_margin_usdt: Optional[float] = None,
|
||||
leverage: Optional[float] = None,
|
||||
deduct_fee: bool = True,
|
||||
) -> Tuple[List[Dict], Dict, List[Dict]]:
|
||||
"""
|
||||
回测主循环。
|
||||
- 若指定 fixed_margin_usdt 与 leverage,则每笔开仓名义价值 = fixed_margin_usdt * leverage(如 50U 一百倍 = 5000U)。
|
||||
- deduct_fee=False 时不扣手续费,总盈利 = 所有 money_pnl 之和。
|
||||
"""
|
||||
if len(klines_5m) < ATR_PERIOD + 50:
|
||||
logger.warning("5分钟K线数量不足")
|
||||
return [], {}, klines_5m
|
||||
|
||||
ema_5m, atr_5m = get_ema_atr_from_klines(klines_5m, EMA_SHORT, ATR_PERIOD)
|
||||
ema_15m_align = align_higher_tf_ema(klines_5m, klines_15m, EMA_MID_FAST, EMA_MID_SLOW)
|
||||
ema_60m_align = align_higher_tf_ema(klines_5m, klines_60m, EMA_LONG_FAST, EMA_LONG_SLOW)
|
||||
volume_ma = build_volume_ma(klines_5m)
|
||||
|
||||
trades: List[Dict] = []
|
||||
position: Optional[Dict] = None
|
||||
last_trade_bar_idx: Optional[int] = None
|
||||
last_close_bar_idx: Optional[int] = None
|
||||
equity_curve: List[float] = [initial_capital]
|
||||
capital = initial_capital
|
||||
# 每笔固定名义价值(USDT):50U 一百倍 = 5000
|
||||
fixed_notional = (fixed_margin_usdt * leverage) if (fixed_margin_usdt is not None and leverage is not None) else None
|
||||
use_fee = deduct_fee
|
||||
fee_fixed = 0 if not use_fee else (FEE_FIXED_BACKTEST if FEE_FIXED_BACKTEST is not None else FEE_FIXED)
|
||||
|
||||
def _size_usdt(cap: float) -> float:
|
||||
if fixed_notional is not None:
|
||||
return fixed_notional
|
||||
return min(cap * MAX_POSITION_PERCENT, cap * BASE_POSITION_PERCENT)
|
||||
|
||||
def _fee(sz: float) -> float:
|
||||
return 0 if not use_fee else (fee_fixed + sz * FEE_RATE * 2)
|
||||
|
||||
for idx in range(ATR_PERIOD, len(klines_5m)):
|
||||
just_reversed = False
|
||||
curr = klines_5m[idx]
|
||||
bar_id = curr["id"]
|
||||
high, low, close = float(curr["high"]), float(curr["low"]), float(curr["close"])
|
||||
atr_val = atr_5m[idx]
|
||||
if atr_val is None or atr_val <= 0:
|
||||
equity_curve.append(capital)
|
||||
continue
|
||||
|
||||
# ---------- 持仓管理:止损 / 止盈 / 移动止损 / 时间止损 ----------
|
||||
if position is not None:
|
||||
pos_dir = position["direction"]
|
||||
entry_price = position["entry_price"]
|
||||
entry_idx = position["entry_bar_idx"]
|
||||
entry_atr = position["entry_atr"]
|
||||
stop_price = position.get("stop_price")
|
||||
trail_activated = position.get("trail_activated", False)
|
||||
exit_reason = None
|
||||
exit_price = close
|
||||
|
||||
if pos_dir == "long":
|
||||
# 硬止损
|
||||
if use_stop_loss and stop_price is not None and low <= stop_price:
|
||||
exit_price = min(stop_price, high)
|
||||
exit_reason = "stop_loss"
|
||||
# 止盈(简化:首次触及任一目标即全平)
|
||||
elif use_take_profit:
|
||||
tp1 = entry_price + entry_atr * TP1_ATR
|
||||
tp2 = entry_price + entry_atr * TP2_ATR
|
||||
tp3 = entry_price + entry_atr * TP3_ATR
|
||||
if high >= tp3:
|
||||
exit_price = tp3
|
||||
exit_reason = "tp3"
|
||||
elif high >= tp2:
|
||||
exit_price = tp2
|
||||
exit_reason = "tp2"
|
||||
elif high >= tp1:
|
||||
exit_price = tp1
|
||||
exit_reason = "tp1"
|
||||
# 移动止损
|
||||
if use_trailing_stop and not exit_reason:
|
||||
if close >= entry_price + entry_atr * TRAIL_START_ATR:
|
||||
trail_activated = True
|
||||
position["trail_activated"] = True
|
||||
trail_stop = close - entry_atr * TRAIL_ATR_MULT
|
||||
if low <= trail_stop:
|
||||
exit_price = trail_stop
|
||||
exit_reason = "trail_stop"
|
||||
# 时间止损
|
||||
if use_time_stop and not exit_reason and (idx - entry_idx) >= TIME_STOP_BARS:
|
||||
if close <= entry_price:
|
||||
exit_price = close
|
||||
exit_reason = "time_stop"
|
||||
else:
|
||||
if use_stop_loss and stop_price is not None and high >= stop_price:
|
||||
exit_price = max(stop_price, low)
|
||||
exit_reason = "stop_loss"
|
||||
elif use_take_profit:
|
||||
tp1 = entry_price - entry_atr * TP1_ATR
|
||||
tp2 = entry_price - entry_atr * TP2_ATR
|
||||
tp3 = entry_price - entry_atr * TP3_ATR
|
||||
if low <= tp3:
|
||||
exit_price = tp3
|
||||
exit_reason = "tp3"
|
||||
elif low <= tp2:
|
||||
exit_price = tp2
|
||||
exit_reason = "tp2"
|
||||
elif low <= tp1:
|
||||
exit_price = tp1
|
||||
exit_reason = "tp1"
|
||||
if use_trailing_stop and not exit_reason:
|
||||
if close <= entry_price - entry_atr * TRAIL_START_ATR:
|
||||
trail_activated = True
|
||||
position["trail_activated"] = True
|
||||
trail_stop = close + entry_atr * TRAIL_ATR_MULT
|
||||
if high >= trail_stop:
|
||||
exit_price = trail_stop
|
||||
exit_reason = "trail_stop"
|
||||
if use_time_stop and not exit_reason and (idx - entry_idx) >= TIME_STOP_BARS:
|
||||
if close >= entry_price:
|
||||
exit_price = close
|
||||
exit_reason = "time_stop"
|
||||
|
||||
if exit_reason:
|
||||
# 平仓
|
||||
if pos_dir == "long":
|
||||
point_pnl = exit_price - entry_price
|
||||
else:
|
||||
point_pnl = entry_price - exit_price
|
||||
size_usdt = position.get("size_usdt", _size_usdt(capital))
|
||||
contract_val = CONTRACT_SIZE / entry_price
|
||||
money_pnl = point_pnl / entry_price * size_usdt
|
||||
fee = _fee(size_usdt)
|
||||
net = money_pnl - fee
|
||||
capital += net
|
||||
trades.append({
|
||||
"direction": "做多" if pos_dir == "long" else "做空",
|
||||
"entry_time": _utc_dt(position["entry_time"]),
|
||||
"exit_time": _utc_dt(bar_id),
|
||||
"entry_price": entry_price,
|
||||
"exit_price": exit_price,
|
||||
"point_pnl": point_pnl,
|
||||
"money_pnl": money_pnl,
|
||||
"fee": fee,
|
||||
"net_profit": net,
|
||||
"exit_reason": exit_reason,
|
||||
"hold_bars": idx - entry_idx,
|
||||
})
|
||||
position = None
|
||||
last_close_bar_idx = idx
|
||||
equity_curve.append(capital)
|
||||
continue
|
||||
|
||||
# ---------- 信号检测 ----------
|
||||
direction, trigger_price, valid_prev_idx, valid_prev = check_trigger(
|
||||
klines_5m, idx, atr_5m, ema_5m, ema_15m_align, ema_60m_align, volume_ma, use_confirm=True
|
||||
)
|
||||
if direction is None:
|
||||
equity_curve.append(capital)
|
||||
continue
|
||||
if SAME_KLINE_NO_REVERSE and last_trade_bar_idx == idx:
|
||||
equity_curve.append(capital)
|
||||
continue
|
||||
if position is not None:
|
||||
if direction == position["direction"]:
|
||||
equity_curve.append(capital)
|
||||
continue
|
||||
# 反手条件
|
||||
bars_since = idx - position["entry_bar_idx"]
|
||||
if bars_since < MIN_BARS_SINCE_ENTRY:
|
||||
equity_curve.append(capital)
|
||||
continue
|
||||
entry_atr_pos = position.get("entry_atr") or atr_val
|
||||
pos_loss_atr = (position["entry_price"] - close) / entry_atr_pos if position["direction"] == "long" else (close - position["entry_price"]) / entry_atr_pos
|
||||
if pos_loss_atr < REVERSE_LOSS_ATR:
|
||||
# 可选:反向突破幅度 > 实体/2 才反手
|
||||
equity_curve.append(capital)
|
||||
continue
|
||||
# 先平仓再开仓(下面统一开仓)
|
||||
# 简化:这里直接平仓记一笔,再开新仓
|
||||
exit_price = close
|
||||
if position["direction"] == "long":
|
||||
point_pnl = exit_price - position["entry_price"]
|
||||
else:
|
||||
point_pnl = position["entry_price"] - exit_price
|
||||
size_usdt = position.get("size_usdt", _size_usdt(capital))
|
||||
money_pnl = point_pnl / position["entry_price"] * size_usdt
|
||||
fee = _fee(size_usdt)
|
||||
net = money_pnl - fee
|
||||
capital += net
|
||||
trades.append({
|
||||
"direction": "做多" if position["direction"] == "long" else "做空",
|
||||
"entry_time": _utc_dt(position["entry_time"]),
|
||||
"exit_time": _utc_dt(bar_id),
|
||||
"entry_price": position["entry_price"],
|
||||
"exit_price": exit_price,
|
||||
"point_pnl": point_pnl,
|
||||
"money_pnl": money_pnl,
|
||||
"fee": fee,
|
||||
"net_profit": net,
|
||||
"exit_reason": "reverse",
|
||||
"hold_bars": idx - position["entry_bar_idx"],
|
||||
})
|
||||
position = None
|
||||
last_close_bar_idx = idx
|
||||
just_reversed = True
|
||||
|
||||
# ---------- 开仓 ----------
|
||||
# 反手后本 K 线允许开仓;否则需间隔 MIN_BARS_BETWEEN_TRADES 根
|
||||
if not just_reversed and last_close_bar_idx is not None and (idx - last_close_bar_idx) < MIN_BARS_BETWEEN_TRADES:
|
||||
equity_curve.append(capital)
|
||||
continue
|
||||
just_reversed = False
|
||||
size_usdt = _size_usdt(capital)
|
||||
if size_usdt <= 0:
|
||||
equity_curve.append(capital)
|
||||
continue
|
||||
stop_price = None
|
||||
if direction == "long":
|
||||
stop_price = trigger_price - atr_val * STOP_LOSS_ATR_MULT
|
||||
else:
|
||||
stop_price = trigger_price + atr_val * STOP_LOSS_ATR_MULT
|
||||
position = {
|
||||
"direction": direction,
|
||||
"entry_price": trigger_price,
|
||||
"entry_time": bar_id,
|
||||
"entry_bar_idx": idx,
|
||||
"entry_atr": atr_val,
|
||||
"stop_price": stop_price,
|
||||
"size_usdt": size_usdt,
|
||||
"closed_ratio": 0,
|
||||
"trail_activated": False,
|
||||
}
|
||||
last_trade_bar_idx = idx
|
||||
equity_curve.append(capital)
|
||||
|
||||
# 尾仓
|
||||
if position is not None:
|
||||
last_bar = klines_5m[-1]
|
||||
exit_price = float(last_bar["close"])
|
||||
pos_dir = position["direction"]
|
||||
entry_price = position["entry_price"]
|
||||
if pos_dir == "long":
|
||||
point_pnl = exit_price - entry_price
|
||||
else:
|
||||
point_pnl = entry_price - exit_price
|
||||
size_usdt = position.get("size_usdt", _size_usdt(capital))
|
||||
money_pnl = point_pnl / entry_price * size_usdt
|
||||
fee = _fee(size_usdt)
|
||||
net = money_pnl - fee
|
||||
capital += net
|
||||
trades.append({
|
||||
"direction": "做多" if pos_dir == "long" else "做空",
|
||||
"entry_time": _utc_dt(position["entry_time"]),
|
||||
"exit_time": _utc_dt(last_bar["id"]),
|
||||
"entry_price": entry_price,
|
||||
"exit_price": exit_price,
|
||||
"point_pnl": point_pnl,
|
||||
"money_pnl": money_pnl,
|
||||
"fee": fee,
|
||||
"net_profit": net,
|
||||
"exit_reason": "tail",
|
||||
"hold_bars": len(klines_5m) - 1 - position["entry_bar_idx"],
|
||||
})
|
||||
|
||||
# 统计
|
||||
total_net = sum(t["net_profit"] for t in trades)
|
||||
total_gross = sum(t["money_pnl"] for t in trades)
|
||||
total_fee = sum(t["fee"] for t in trades)
|
||||
win_count = len([t for t in trades if t["net_profit"] > 0])
|
||||
stats = {
|
||||
"total_trades": len(trades),
|
||||
"win_count": win_count,
|
||||
"win_rate": (win_count / len(trades) * 100) if trades else 0,
|
||||
"total_gross_profit": total_gross,
|
||||
"total_fee": total_fee,
|
||||
"total_net_profit": total_net,
|
||||
"final_capital": capital,
|
||||
"max_drawdown": 0,
|
||||
"max_drawdown_pct": 0,
|
||||
}
|
||||
peak = initial_capital
|
||||
for eq in equity_curve:
|
||||
peak = max(peak, eq)
|
||||
dd = peak - eq
|
||||
if peak > 0:
|
||||
stats["max_drawdown"] = max(stats["max_drawdown"], dd)
|
||||
stats["max_drawdown_pct"] = max(stats["max_drawdown_pct"], dd / peak * 100)
|
||||
return trades, stats, klines_5m
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description="自适应三分位趋势策略回测")
|
||||
parser.add_argument("--start", default="2025-01-01", help="开始日期 YYYY-MM-DD")
|
||||
parser.add_argument("--end", default="2025-12-31", help="结束日期,默认 2025-12-31")
|
||||
parser.add_argument("--data-dir", default=None, help="数据目录,默认 adaptive_third_strategy/data")
|
||||
parser.add_argument("--no-api", action="store_true", help="不从 API 拉取,仅用本地 CSV")
|
||||
parser.add_argument("--capital", type=float, default=10000, help="初始资金(按比例开仓时用)")
|
||||
parser.add_argument("--fixed-margin", type=float, default=None, help="每笔固定保证金 USDT,如 50")
|
||||
parser.add_argument("--leverage", type=float, default=None, help="杠杆倍数,如 100")
|
||||
parser.add_argument("--no-fee", action="store_true", help="不扣手续费,只算总盈利")
|
||||
args = parser.parse_args()
|
||||
start_dt = datetime.datetime.strptime(args.start, "%Y-%m-%d")
|
||||
if args.end:
|
||||
end_dt = datetime.datetime.strptime(args.end, "%Y-%m-%d")
|
||||
# 包含 end 日全天:取次日 0 点前一刻,这样 id < end_ts 的 K 线都含在内
|
||||
end_ts = int((end_dt + datetime.timedelta(days=1)).timestamp())
|
||||
else:
|
||||
end_ts = int(datetime.datetime.utcnow().timestamp())
|
||||
start_ts = int(start_dt.timestamp())
|
||||
data_dir = args.data_dir or os.path.join(SCRIPT_DIR, "data")
|
||||
k5, k15, k60 = ensure_data(start_ts, end_ts, data_dir, use_api=not args.no_api)
|
||||
if not k5:
|
||||
logger.error("无 5 分钟数据,请先抓取或开启 --api")
|
||||
return
|
||||
logger.info(f"5m={len(k5)} 15m={len(k15)} 60m={len(k60)}")
|
||||
trades, stats, _ = run_backtest(
|
||||
k5, k15, k60,
|
||||
initial_capital=args.capital,
|
||||
fixed_margin_usdt=args.fixed_margin,
|
||||
leverage=args.leverage,
|
||||
deduct_fee=not args.no_fee,
|
||||
)
|
||||
logger.info(f"交易笔数: {stats['total_trades']} 胜率: {stats['win_rate']:.2f}% "
|
||||
f"总盈利(未扣费): {stats['total_gross_profit']:.2f} USDT "
|
||||
f"总手续费: {stats['total_fee']:.2f} 总净利润: {stats['total_net_profit']:.2f} "
|
||||
f"最大回撤: {stats['max_drawdown']:.2f} ({stats['max_drawdown_pct']:.2f}%)")
|
||||
out_csv = os.path.join(SCRIPT_DIR, "backtest_trades.csv")
|
||||
if trades:
|
||||
with open(out_csv, "w", newline="", encoding="utf-8") as f:
|
||||
w = csv.DictWriter(f, fieldnames=["direction", "entry_time", "exit_time", "entry_price", "exit_price", "point_pnl", "money_pnl", "fee", "net_profit", "exit_reason", "hold_bars"])
|
||||
w.writeheader()
|
||||
for t in trades:
|
||||
w.writerow({k: str(v) if isinstance(v, datetime.datetime) else v for k, v in t.items()})
|
||||
logger.info(f"交易记录已保存: {out_csv}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,97 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
自适应三分位趋势策略 - 配置参数
|
||||
"""
|
||||
|
||||
# ========== 数据与周期 ==========
|
||||
CONTRACT_SYMBOL = "ETHUSDT"
|
||||
STEP_5M = 5
|
||||
STEP_15M = 15
|
||||
STEP_60M = 60
|
||||
|
||||
# 有效K线标准:实体 >= max(ATR(20)*0.1, 最小波动阈值)
|
||||
MIN_BODY_ATR_RATIO = 0.1
|
||||
MIN_VOLATILITY_PERCENT = 0.0005 # 当前价格 × 0.05%
|
||||
|
||||
# ========== 趋势判断(EMA周期) ==========
|
||||
# 长期:1小时 EMA(50) vs EMA(200)
|
||||
EMA_LONG_FAST = 50
|
||||
EMA_LONG_SLOW = 200
|
||||
# 中期:15分钟 EMA(20) vs EMA(50)
|
||||
EMA_MID_FAST = 20
|
||||
EMA_MID_SLOW = 50
|
||||
# 短期:5分钟 EMA(9)
|
||||
EMA_SHORT = 9
|
||||
|
||||
# 交易模式:aggressive / conservative / strict
|
||||
TREND_MODE = "conservative"
|
||||
|
||||
# ========== 动态触发 ==========
|
||||
ATR_PERIOD = 20
|
||||
VOLATILITY_COEF_CLAMP = (0.3, 3.0) # 波动率系数范围
|
||||
BASE_COEF = 0.33
|
||||
# 趋势方向系数(顺势更易触发)
|
||||
TREND_FAVOR_COEF = 0.8
|
||||
TREND_AGAINST_COEF = 1.2
|
||||
|
||||
# ========== 信号确认(至少满足几项) ==========
|
||||
CONFIRM_REQUIRED = 2
|
||||
VOLUME_MA_PERIOD = 20
|
||||
VOLUME_RATIO_THRESHOLD = 0.8 # 成交量 > 前20根均量 × 0.8
|
||||
|
||||
# ========== 风险管理 ==========
|
||||
MAX_POSITION_PERCENT = 0.10 # 单笔最大 10%
|
||||
BASE_POSITION_PERCENT = 0.02 # 基础仓位 2%
|
||||
STOP_LOSS_ATR_MULT = 2.0 # 硬止损 = ATR × 2.0
|
||||
TIME_STOP_BARS = 3 # 3根K线未盈利平仓
|
||||
TRAIL_START_ATR = 1.0 # 盈利 1×ATR 后启动移动止损
|
||||
TRAIL_ATR_MULT = 0.5 # 移动止损距离 0.5×ATR
|
||||
# 止盈目标(ATR倍数)与分批比例
|
||||
TP1_ATR = 1.5
|
||||
TP2_ATR = 3.0
|
||||
TP3_ATR = 5.0
|
||||
TP1_RATIO = 0.30
|
||||
TP2_RATIO = 0.30
|
||||
TP3_RATIO = 0.40
|
||||
|
||||
# ========== 反手条件 ==========
|
||||
REVERSE_BREAK_MULT = 0.5 # 突破幅度 > 实体/2
|
||||
REVERSE_LOSS_ATR = 0.5 # 当前持仓亏损 > 0.5×ATR 可反手
|
||||
MIN_BARS_SINCE_ENTRY = 2 # 距离上次开仓至少2根K线
|
||||
SAME_KLINE_NO_REVERSE = True # 同一K线不反手
|
||||
|
||||
# ========== 市场状态调整 ==========
|
||||
# 强趋势 / 震荡 / 高波动 时的系数
|
||||
STRONG_TREND_COEF = 0.25
|
||||
STRONG_TREND_STOP_ATR = 3.0
|
||||
RANGE_COEF = 0.4
|
||||
RANGE_STOP_ATR = 1.5
|
||||
HIGH_VOL_POSITION_COEF = 0.5
|
||||
HIGH_VOL_EXTRA_CONFIRM = 1
|
||||
|
||||
# ========== 禁止交易时段(UTC+8 小时:分) ==========
|
||||
FORBIDDEN_PERIODS = [
|
||||
(0, 0, 0, 30), # 00:00-00:30
|
||||
(8, 0, 8, 30), # 08:00-08:30
|
||||
(20, 0, 20, 30), # 20:00-20:30
|
||||
]
|
||||
# 高波动暂停:ATR > 平均ATR × 2 暂停
|
||||
ATR_PAUSE_MULT = 2.0
|
||||
|
||||
# ========== 自适应绩效 ==========
|
||||
CONSECUTIVE_LOSS_PAUSE = 3 # 连续亏损3次仓位减半
|
||||
WIN_RATE_LOOKBACK = 20
|
||||
WIN_RATE_PAUSE_THRESHOLD = 0.40 # 胜率<40% 暂停
|
||||
MAX_DRAWDOWN_PERCENT = 0.10 # 最大回撤10% 观察期
|
||||
MAX_TRADES_PER_DAY = 20
|
||||
MAX_HOLD_BARS = 4 * 12 # 4小时 ≈ 4*12 根5分钟
|
||||
|
||||
# ========== 回测/实盘通用 ==========
|
||||
CONTRACT_SIZE = 10000
|
||||
SLIPPAGE_POINTS = 3
|
||||
FEE_RATE = 0.0005
|
||||
FEE_FIXED = 5
|
||||
# 回测专用:固定手续费(合约多为按比例,可设为 0 观察策略本身)
|
||||
FEE_FIXED_BACKTEST = 0
|
||||
# 两笔开仓之间至少间隔 N 根 5 分钟 K 线,减少过度交易
|
||||
MIN_BARS_BETWEEN_TRADES = 4
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,697 +0,0 @@
|
||||
id,open,high,low,close,volume
|
||||
1735660800,3412.6,3416.0,3390.39,3408.23,102369440.0
|
||||
1735664400,3408.23,3410.38,3329.45,3362.6,239948902.0
|
||||
1735668000,3362.62,3372.14,3349.24,3360.3,87820990.0
|
||||
1735671600,3360.3,3367.9,3342.85,3354.68,42695570.0
|
||||
1735675200,3354.68,3364.47,3336.84,3346.12,54111736.0
|
||||
1735678800,3345.76,3354.28,3331.51,3351.72,48624216.0
|
||||
1735682400,3351.66,3351.66,3327.8,3338.94,44710804.0
|
||||
1735686000,3338.94,3345.03,3327.22,3336.37,37042226.0
|
||||
1735689600,3336.47,3364.49,3334.99,3362.89,57465298.0
|
||||
1735693200,3362.89,3365.08,3341.6,3345.83,34711202.0
|
||||
1735696800,3345.83,3368.02,3345.35,3361.81,34405766.0
|
||||
1735700400,3361.81,3363.0,3350.22,3354.34,21508812.0
|
||||
1735704000,3354.34,3355.78,3338.4,3340.12,45813072.0
|
||||
1735707600,3340.04,3350.57,3339.13,3344.2,29504628.0
|
||||
1735711200,3344.2,3347.33,3335.57,3345.65,26979022.0
|
||||
1735714800,3345.65,3347.77,3338.32,3346.0,19122736.0
|
||||
1735718400,3345.94,3348.0,3330.48,3335.57,39528308.0
|
||||
1735722000,3335.57,3340.16,3313.08,3333.4,129899100.0
|
||||
1735725600,3333.52,3337.99,3325.01,3325.99,32584622.0
|
||||
1735729200,3325.99,3347.73,3322.8,3340.07,45465420.0
|
||||
1735732800,3340.07,3351.21,3337.29,3346.01,39172620.0
|
||||
1735736400,3346.01,3352.78,3331.45,3343.1,51120458.0
|
||||
1735740000,3342.83,3358.89,3327.15,3345.0,82299724.0
|
||||
1735743600,3345.06,3354.24,3332.01,3352.07,51286002.0
|
||||
1735747200,3352.07,3356.14,3331.27,3337.72,49132440.0
|
||||
1735750800,3337.72,3344.37,3322.56,3343.02,49046866.0
|
||||
1735754400,3343.01,3351.47,3332.38,3347.81,36271786.0
|
||||
1735758000,3347.81,3363.82,3338.02,3356.2,55282332.0
|
||||
1735761600,3356.2,3361.76,3351.0,3359.39,38099614.0
|
||||
1735765200,3359.28,3370.87,3352.49,3367.59,51552628.0
|
||||
1735768800,3367.59,3373.97,3357.81,3360.65,43553722.0
|
||||
1735772400,3360.65,3365.99,3352.07,3359.35,33197468.0
|
||||
1735776000,3359.35,3399.41,3354.01,3389.27,123638482.0
|
||||
1735779600,3389.27,3404.76,3377.72,3383.02,72528314.0
|
||||
1735783200,3383.1,3397.9,3376.89,3397.89,35142436.0
|
||||
1735786800,3397.89,3401.77,3386.57,3388.01,28707566.0
|
||||
1735790400,3388.01,3427.38,3387.4,3417.81,124752266.0
|
||||
1735794000,3417.82,3419.18,3404.4,3412.37,64463210.0
|
||||
1735797600,3412.63,3415.73,3401.82,3410.19,37061074.0
|
||||
1735801200,3410.19,3421.77,3408.6,3419.68,42976506.0
|
||||
1735804800,3419.68,3442.8,3414.59,3437.98,81383320.0
|
||||
1735808400,3437.13,3470.72,3433.94,3467.93,117165150.0
|
||||
1735812000,3467.93,3476.09,3457.49,3468.61,95625752.0
|
||||
1735815600,3468.61,3474.99,3457.1,3470.47,55981282.0
|
||||
1735819200,3470.46,3480.37,3465.1,3479.77,71134738.0
|
||||
1735822800,3479.89,3483.94,3459.45,3465.33,135921684.0
|
||||
1735826400,3465.33,3509.87,3439.82,3494.61,298463722.0
|
||||
1735830000,3494.61,3495.56,3455.45,3462.76,137324232.0
|
||||
1735833600,3462.76,3488.75,3447.84,3468.71,102691858.0
|
||||
1735837200,3468.7,3478.07,3429.2,3449.77,130762240.0
|
||||
1735840800,3449.66,3464.67,3439.64,3464.16,96730060.0
|
||||
1735844400,3464.16,3473.57,3458.11,3462.31,52010818.0
|
||||
1735848000,3462.31,3474.61,3450.9,3452.17,41220990.0
|
||||
1735851600,3452.29,3455.39,3442.18,3453.73,48633000.0
|
||||
1735855200,3453.73,3453.75,3419.06,3440.0,62033228.0
|
||||
1735858800,3439.91,3456.41,3437.0,3454.81,33740154.0
|
||||
1735862400,3454.81,3466.23,3445.58,3448.8,49005198.0
|
||||
1735866000,3448.8,3465.02,3442.11,3464.36,64836976.0
|
||||
1735869600,3464.36,3476.32,3455.92,3456.74,51253786.0
|
||||
1735873200,3456.64,3471.96,3455.04,3469.22,41861896.0
|
||||
1735876800,3469.21,3470.11,3455.0,3457.65,33613294.0
|
||||
1735880400,3457.85,3459.99,3443.71,3455.13,57285134.0
|
||||
1735884000,3455.13,3460.9,3441.01,3441.9,36792172.0
|
||||
1735887600,3441.9,3443.62,3431.19,3432.52,64944146.0
|
||||
1735891200,3432.52,3441.96,3422.35,3429.01,71555590.0
|
||||
1735894800,3429.01,3449.99,3428.0,3442.67,45692272.0
|
||||
1735898400,3442.66,3451.63,3437.26,3444.82,34326568.0
|
||||
1735902000,3444.82,3466.53,3444.82,3464.73,51124624.0
|
||||
1735905600,3464.47,3475.49,3460.01,3462.58,57106556.0
|
||||
1735909200,3462.53,3496.26,3462.05,3480.31,136074052.0
|
||||
1735912800,3480.36,3535.47,3479.85,3525.73,252612796.0
|
||||
1735916400,3527.99,3575.99,3518.91,3564.31,266749252.0
|
||||
1735920000,3564.31,3598.54,3564.2,3583.68,236728242.0
|
||||
1735923600,3583.76,3593.0,3572.94,3574.28,77062022.0
|
||||
1735927200,3574.28,3616.82,3573.18,3604.62,145063892.0
|
||||
1735930800,3604.53,3629.68,3601.44,3621.89,136264822.0
|
||||
1735934400,3621.89,3622.32,3599.55,3600.11,82316038.0
|
||||
1735938000,3600.2,3615.64,3594.35,3613.51,60389536.0
|
||||
1735941600,3613.51,3623.2,3609.21,3615.11,41030186.0
|
||||
1735945200,3615.11,3616.87,3603.44,3607.89,55205762.0
|
||||
1735948800,3607.89,3619.9,3601.06,3601.96,52305142.0
|
||||
1735952400,3601.96,3603.52,3581.77,3595.65,60263730.0
|
||||
1735956000,3595.65,3599.45,3582.0,3595.42,35032408.0
|
||||
1735959600,3595.42,3599.48,3590.01,3597.04,28343560.0
|
||||
1735963200,3597.08,3597.18,3583.1,3590.9,31587898.0
|
||||
1735966800,3590.9,3596.15,3585.87,3595.52,27817296.0
|
||||
1735970400,3595.52,3606.6,3594.06,3599.16,34022310.0
|
||||
1735974000,3599.16,3609.0,3591.89,3593.04,43060454.0
|
||||
1735977600,3593.04,3603.11,3584.0,3584.17,82921978.0
|
||||
1735981200,3584.06,3594.99,3582.48,3588.57,39073846.0
|
||||
1735984800,3588.57,3591.98,3570.92,3587.1,48068340.0
|
||||
1735988400,3587.1,3605.05,3583.83,3601.16,55657250.0
|
||||
1735992000,3601.16,3644.09,3599.35,3634.42,152503238.0
|
||||
1735995600,3634.42,3648.59,3626.28,3643.77,105667482.0
|
||||
1735999200,3643.77,3646.0,3631.36,3636.4,63779260.0
|
||||
1736002800,3636.4,3662.37,3592.27,3606.5,238821272.0
|
||||
1736006400,3606.5,3627.64,3600.3,3620.72,76957118.0
|
||||
1736010000,3620.89,3633.52,3618.89,3632.52,40292730.0
|
||||
1736013600,3632.52,3643.7,3618.0,3625.99,47967500.0
|
||||
1736017200,3625.99,3638.39,3623.02,3629.81,24410624.0
|
||||
1736020800,3629.81,3667.58,3629.81,3659.31,70469288.0
|
||||
1736024400,3659.31,3669.95,3653.82,3657.57,60929522.0
|
||||
1736028000,3657.57,3662.13,3645.68,3658.81,34926154.0
|
||||
1736031600,3658.81,3664.88,3649.92,3655.98,35246640.0
|
||||
1736035200,3655.98,3674.56,3646.87,3648.66,55777438.0
|
||||
1736038800,3648.66,3650.3,3630.11,3635.55,52527572.0
|
||||
1736042400,3635.55,3644.01,3633.69,3640.11,24195566.0
|
||||
1736046000,3640.11,3640.9,3628.03,3636.5,20439542.0
|
||||
1736049600,3636.5,3648.0,3635.87,3642.88,29199868.0
|
||||
1736053200,3642.88,3642.9,3629.28,3631.5,26183660.0
|
||||
1736056800,3631.5,3640.66,3626.93,3637.54,20101968.0
|
||||
1736060400,3637.54,3640.13,3628.97,3630.38,19147964.0
|
||||
1736064000,3630.38,3632.11,3607.51,3618.97,102512590.0
|
||||
1736067600,3618.97,3622.41,3604.01,3608.53,47549468.0
|
||||
1736071200,3608.65,3617.6,3601.63,3614.68,40940760.0
|
||||
1736074800,3614.68,3620.13,3608.33,3612.57,30546036.0
|
||||
1736078400,3612.57,3621.77,3603.95,3612.88,43155640.0
|
||||
1736082000,3613.04,3626.65,3611.35,3613.49,37524524.0
|
||||
1736085600,3613.49,3627.36,3592.53,3621.73,135663724.0
|
||||
1736089200,3621.65,3637.67,3618.01,3628.43,75103806.0
|
||||
1736092800,3628.43,3640.01,3623.01,3627.1,60533890.0
|
||||
1736096400,3627.1,3647.35,3621.0,3627.36,52375452.0
|
||||
1736100000,3627.14,3632.37,3611.43,3629.47,43996958.0
|
||||
1736103600,3629.47,3638.96,3625.0,3636.9,25938496.0
|
||||
1736107200,3636.9,3657.0,3627.34,3634.52,61275846.0
|
||||
1736110800,3634.52,3647.21,3632.96,3644.52,23260872.0
|
||||
1736114400,3644.52,3648.89,3633.14,3644.18,23637400.0
|
||||
1736118000,3644.15,3654.47,3631.56,3634.85,35985516.0
|
||||
1736121600,3634.85,3654.52,3620.5,3620.5,61317924.0
|
||||
1736125200,3620.67,3654.84,3608.95,3647.81,86153808.0
|
||||
1736128800,3647.51,3684.08,3644.14,3657.59,182952938.0
|
||||
1736132400,3657.5,3673.87,3652.98,3663.93,46604218.0
|
||||
1736136000,3663.93,3678.2,3659.69,3671.82,46449432.0
|
||||
1736139600,3671.82,3697.99,3666.17,3679.61,99035916.0
|
||||
1736143200,3679.61,3684.04,3664.12,3664.87,52617428.0
|
||||
1736146800,3664.87,3667.1,3641.26,3649.39,93646214.0
|
||||
1736150400,3649.39,3659.4,3635.02,3639.98,82925846.0
|
||||
1736154000,3639.98,3649.36,3639.06,3643.89,28971374.0
|
||||
1736157600,3643.89,3645.87,3631.43,3637.07,42695740.0
|
||||
1736161200,3637.07,3658.44,3635.1,3653.7,56217424.0
|
||||
1736164800,3653.7,3661.82,3640.68,3641.68,59441302.0
|
||||
1736168400,3641.68,3651.94,3623.81,3650.0,96201734.0
|
||||
1736172000,3650.0,3702.32,3621.12,3684.3,289303830.0
|
||||
1736175600,3684.26,3713.01,3670.27,3711.59,260080200.0
|
||||
1736179200,3712.89,3743.7,3690.41,3709.39,272849142.0
|
||||
1736182800,3709.39,3710.16,3654.92,3672.78,176868982.0
|
||||
1736186400,3672.78,3691.03,3661.17,3681.2,68879960.0
|
||||
1736190000,3681.31,3694.94,3674.38,3686.47,39532406.0
|
||||
1736193600,3686.47,3702.68,3671.65,3682.33,59308086.0
|
||||
1736197200,3682.07,3687.78,3665.39,3666.31,51081846.0
|
||||
1736200800,3666.31,3682.56,3666.1,3677.43,38055272.0
|
||||
1736204400,3677.43,3686.36,3667.4,3685.72,28287998.0
|
||||
1736208000,3685.72,3693.4,3672.22,3674.01,48983234.0
|
||||
1736211600,3674.01,3695.78,3661.96,3693.68,48454388.0
|
||||
1736215200,3693.68,3699.99,3675.01,3676.88,41902274.0
|
||||
1736218800,3676.87,3687.99,3672.81,3681.48,34679266.0
|
||||
1736222400,3681.48,3685.53,3672.38,3672.4,22079898.0
|
||||
1736226000,3672.4,3676.15,3657.64,3668.64,45811008.0
|
||||
1736229600,3668.64,3674.79,3663.01,3671.53,29019498.0
|
||||
1736233200,3671.53,3678.99,3661.35,3666.6,29153142.0
|
||||
1736236800,3666.6,3678.49,3659.26,3673.4,41854680.0
|
||||
1736240400,3673.4,3675.58,3652.21,3660.13,57671204.0
|
||||
1736244000,3660.14,3665.0,3650.39,3657.01,42289288.0
|
||||
1736247600,3657.01,3657.99,3625.07,3638.34,132520824.0
|
||||
1736251200,3638.6,3642.32,3625.51,3632.68,67571326.0
|
||||
1736254800,3632.68,3644.9,3630.15,3635.14,48976982.0
|
||||
1736258400,3635.2,3641.18,3523.0,3567.07,333440168.0
|
||||
1736262000,3567.07,3580.49,3410.38,3461.46,1085438710.0
|
||||
1736265600,3461.33,3490.42,3447.53,3456.8,250533314.0
|
||||
1736269200,3456.8,3469.33,3420.12,3441.09,227652302.0
|
||||
1736272800,3440.99,3458.14,3411.8,3420.51,182306610.0
|
||||
1736276400,3420.51,3436.46,3374.36,3384.93,267051616.0
|
||||
1736280000,3384.59,3399.26,3362.16,3395.32,175652002.0
|
||||
1736283600,3395.27,3412.8,3359.81,3361.96,139760754.0
|
||||
1736287200,3362.09,3396.25,3355.13,3395.21,96223134.0
|
||||
1736290800,3395.21,3395.21,3373.61,3379.37,51490032.0
|
||||
1736294400,3379.37,3413.55,3376.91,3410.01,87357960.0
|
||||
1736298000,3410.09,3410.12,3373.39,3389.82,70746830.0
|
||||
1736301600,3389.82,3402.98,3381.2,3381.23,51129254.0
|
||||
1736305200,3381.23,3386.39,3342.71,3359.34,186577674.0
|
||||
1736308800,3359.34,3372.73,3342.07,3347.27,105866768.0
|
||||
1736312400,3347.27,3371.89,3336.08,3350.48,104936154.0
|
||||
1736316000,3350.48,3361.34,3306.03,3307.77,211327564.0
|
||||
1736319600,3307.8,3358.07,3306.71,3353.19,146187366.0
|
||||
1736323200,3353.19,3375.59,3349.83,3364.56,101254772.0
|
||||
1736326800,3364.57,3372.85,3345.0,3345.89,75539896.0
|
||||
1736330400,3345.89,3371.99,3345.16,3360.9,69829694.0
|
||||
1736334000,3360.9,3366.39,3321.76,3348.44,164453210.0
|
||||
1736337600,3348.44,3359.3,3337.6,3338.59,85930986.0
|
||||
1736341200,3338.61,3365.29,3338.61,3357.39,89649230.0
|
||||
1736344800,3357.21,3380.52,3346.3,3369.27,151342344.0
|
||||
1736348400,3368.94,3384.66,3332.0,3345.99,206886030.0
|
||||
1736352000,3345.99,3347.06,3310.44,3313.07,148369138.0
|
||||
1736355600,3313.11,3318.93,3207.46,3262.23,658990620.0
|
||||
1736359200,3262.21,3329.38,3257.5,3281.58,286190748.0
|
||||
1736362800,3281.99,3308.69,3270.47,3279.62,140264688.0
|
||||
1736366400,3279.57,3290.0,3265.01,3284.51,65621974.0
|
||||
1736370000,3284.51,3303.67,3268.87,3297.9,74963226.0
|
||||
1736373600,3297.85,3332.86,3297.28,3331.31,80470748.0
|
||||
1736377200,3331.31,3333.47,3314.73,3325.46,41780554.0
|
||||
1736380800,3325.46,3342.07,3316.67,3340.7,60166482.0
|
||||
1736384400,3340.12,3356.49,3333.9,3344.64,63614256.0
|
||||
1736388000,3344.64,3349.59,3305.94,3339.6,165240584.0
|
||||
1736391600,3339.6,3341.35,3315.56,3324.06,63469448.0
|
||||
1736395200,3323.71,3342.24,3311.0,3339.0,67512222.0
|
||||
1736398800,3338.96,3341.19,3323.42,3327.38,39150924.0
|
||||
1736402400,3327.38,3333.77,3313.68,3314.92,41707280.0
|
||||
1736406000,3314.92,3323.33,3281.3,3288.19,154505570.0
|
||||
1736409600,3288.19,3325.89,3263.38,3308.99,192210656.0
|
||||
1736413200,3309.07,3326.72,3305.94,3316.68,65498146.0
|
||||
1736416800,3316.68,3321.07,3283.42,3310.84,106064710.0
|
||||
1736420400,3310.84,3325.5,3292.07,3300.36,76143062.0
|
||||
1736424000,3300.36,3309.23,3279.89,3294.89,72951552.0
|
||||
1736427600,3294.89,3297.76,3211.02,3219.8,310900906.0
|
||||
1736431200,3219.6,3251.76,3209.02,3249.62,207212678.0
|
||||
1736434800,3249.62,3335.0,3242.45,3318.81,240337982.0
|
||||
1736438400,3318.81,3326.2,3286.7,3300.79,164041718.0
|
||||
1736442000,3300.79,3302.94,3234.38,3249.49,184871012.0
|
||||
1736445600,3249.49,3269.11,3244.47,3258.78,103697430.0
|
||||
1736449200,3259.07,3267.62,3190.01,3193.56,229460404.0
|
||||
1736452800,3193.54,3212.74,3155.96,3194.13,302442148.0
|
||||
1736456400,3194.12,3224.91,3189.44,3206.78,90139568.0
|
||||
1736460000,3205.89,3238.13,3186.39,3222.7,89771046.0
|
||||
1736463600,3222.7,3231.72,3208.6,3217.86,49759112.0
|
||||
1736467200,3218.13,3232.61,3212.86,3221.74,54670720.0
|
||||
1736470800,3221.74,3238.89,3212.52,3235.53,61826016.0
|
||||
1736474400,3235.53,3258.78,3229.27,3239.91,73960624.0
|
||||
1736478000,3240.12,3254.85,3237.3,3253.05,39189052.0
|
||||
1736481600,3253.05,3263.57,3245.37,3252.64,52641914.0
|
||||
1736485200,3252.78,3280.15,3241.27,3262.78,81991616.0
|
||||
1736488800,3262.6,3279.35,3258.56,3276.45,46459632.0
|
||||
1736492400,3276.45,3312.38,3269.49,3294.65,142743074.0
|
||||
1736496000,3294.65,3300.61,3286.36,3294.77,54960336.0
|
||||
1736499600,3294.77,3309.3,3289.02,3301.2,55245834.0
|
||||
1736503200,3301.2,3319.92,3296.47,3316.89,63228784.0
|
||||
1736506800,3316.89,3318.66,3294.49,3309.74,69342416.0
|
||||
1736510400,3309.74,3311.0,3291.98,3309.36,67812524.0
|
||||
1736514000,3309.25,3315.95,3215.36,3242.94,442746528.0
|
||||
1736517600,3242.94,3277.64,3235.03,3255.47,229753402.0
|
||||
1736521200,3253.81,3274.86,3193.21,3243.69,363287258.0
|
||||
1736524800,3243.54,3260.64,3231.0,3246.19,141134442.0
|
||||
1736528400,3246.11,3320.22,3241.43,3313.36,173307620.0
|
||||
1736532000,3313.27,3313.27,3280.14,3285.21,117654418.0
|
||||
1736535600,3285.76,3297.7,3269.11,3293.79,84001780.0
|
||||
1736539200,3293.79,3293.8,3256.27,3258.81,62458836.0
|
||||
1736542800,3258.71,3273.33,3256.01,3264.13,32978326.0
|
||||
1736546400,3264.13,3280.1,3258.48,3272.36,35630396.0
|
||||
1736550000,3272.36,3274.17,3263.04,3265.52,22914060.0
|
||||
1736553600,3265.52,3268.92,3256.51,3264.4,38075382.0
|
||||
1736557200,3264.4,3266.68,3235.13,3244.52,61301216.0
|
||||
1736560800,3244.52,3250.87,3236.17,3246.5,29747936.0
|
||||
1736564400,3246.5,3247.11,3230.52,3235.88,28924308.0
|
||||
1736568000,3235.88,3242.0,3228.51,3237.53,32303354.0
|
||||
1736571600,3237.53,3244.92,3231.48,3244.35,33205960.0
|
||||
1736575200,3244.35,3249.5,3236.31,3236.33,28510900.0
|
||||
1736578800,3236.33,3243.13,3224.17,3234.72,38537850.0
|
||||
1736582400,3234.72,3239.07,3215.12,3223.37,48376530.0
|
||||
1736586000,3223.37,3243.45,3222.27,3243.01,49518806.0
|
||||
1736589600,3243.01,3255.31,3242.22,3255.26,79756560.0
|
||||
1736593200,3255.26,3279.23,3252.9,3272.68,95103116.0
|
||||
1736596800,3272.68,3280.0,3266.71,3271.65,39652500.0
|
||||
1736600400,3271.65,3274.82,3257.51,3266.4,49449588.0
|
||||
1736604000,3266.4,3278.98,3257.26,3277.3,42866688.0
|
||||
1736607600,3277.2,3281.01,3265.16,3270.07,31206676.0
|
||||
1736611200,3270.07,3277.14,3261.0,3274.11,31282780.0
|
||||
1736614800,3274.11,3274.9,3257.7,3270.89,40054362.0
|
||||
1736618400,3270.89,3279.01,3266.11,3277.06,18851470.0
|
||||
1736622000,3276.96,3282.49,3268.28,3277.47,31217294.0
|
||||
1736625600,3277.47,3299.48,3274.61,3298.28,40594708.0
|
||||
1736629200,3298.28,3319.01,3298.05,3305.69,62717802.0
|
||||
1736632800,3305.69,3307.36,3284.5,3287.56,40714280.0
|
||||
1736636400,3287.56,3293.32,3280.33,3281.85,29895820.0
|
||||
1736640000,3281.85,3286.11,3277.98,3282.18,22834780.0
|
||||
1736643600,3282.18,3288.32,3273.02,3287.87,25636570.0
|
||||
1736647200,3287.87,3288.96,3278.26,3288.87,17973236.0
|
||||
1736650800,3288.87,3289.88,3277.02,3285.27,19745114.0
|
||||
1736654400,3285.27,3288.62,3279.52,3282.52,17536690.0
|
||||
1736658000,3282.52,3284.4,3273.11,3278.51,18182922.0
|
||||
1736661600,3278.51,3280.29,3266.38,3269.24,28730928.0
|
||||
1736665200,3269.24,3272.82,3262.31,3265.63,38470516.0
|
||||
1736668800,3265.62,3270.61,3233.02,3238.36,122652126.0
|
||||
1736672400,3238.45,3243.5,3223.13,3236.97,107510624.0
|
||||
1736676000,3236.97,3244.82,3232.45,3243.12,47696088.0
|
||||
1736679600,3243.12,3257.11,3239.67,3251.63,50138698.0
|
||||
1736683200,3251.86,3264.27,3246.51,3249.24,57619188.0
|
||||
1736686800,3249.24,3270.58,3246.94,3266.0,68944450.0
|
||||
1736690400,3266.0,3279.9,3265.0,3267.83,73240184.0
|
||||
1736694000,3267.83,3294.5,3264.0,3284.65,79888684.0
|
||||
1736697600,3284.65,3286.99,3267.61,3280.51,43169660.0
|
||||
1736701200,3280.51,3291.46,3277.19,3287.9,46143554.0
|
||||
1736704800,3287.81,3298.89,3279.33,3283.73,47041518.0
|
||||
1736708400,3283.73,3287.97,3274.66,3279.11,25800656.0
|
||||
1736712000,3279.11,3288.49,3272.07,3280.55,23003516.0
|
||||
1736715600,3280.63,3281.94,3262.59,3265.11,44126248.0
|
||||
1736719200,3265.12,3267.41,3232.85,3239.17,101407078.0
|
||||
1736722800,3239.17,3267.36,3237.68,3265.69,44761358.0
|
||||
1736726400,3265.69,3338.19,3263.9,3307.67,252955806.0
|
||||
1736730000,3307.43,3311.66,3251.27,3260.13,196644388.0
|
||||
1736733600,3260.13,3269.95,3236.0,3254.9,121374346.0
|
||||
1736737200,3254.9,3265.89,3234.02,3241.24,90367330.0
|
||||
1736740800,3241.24,3259.58,3236.46,3237.36,78938608.0
|
||||
1736744400,3237.36,3237.8,3211.68,3224.18,119089336.0
|
||||
1736748000,3224.49,3232.3,3180.01,3190.13,176832932.0
|
||||
1736751600,3190.1,3208.74,3181.93,3200.53,86710800.0
|
||||
1736755200,3200.53,3205.45,3159.9,3168.74,156073064.0
|
||||
1736758800,3168.74,3176.15,3119.18,3155.01,234976700.0
|
||||
1736762400,3155.01,3155.99,3106.07,3117.96,317611886.0
|
||||
1736766000,3118.75,3123.04,3055.09,3056.7,378759676.0
|
||||
1736769600,3056.4,3084.95,3039.54,3045.72,305548852.0
|
||||
1736773200,3045.89,3090.3,3032.3,3059.44,249979524.0
|
||||
1736776800,3059.44,3060.11,2909.6,3033.47,974243346.0
|
||||
1736780400,3033.59,3076.35,3025.13,3047.42,366822882.0
|
||||
1736784000,3047.42,3058.66,3004.86,3008.65,234479378.0
|
||||
1736787600,3007.87,3035.84,3003.01,3019.36,152995168.0
|
||||
1736791200,3019.36,3032.98,2985.02,3006.39,179997976.0
|
||||
1736794800,3006.38,3036.47,2991.8,3023.97,125369102.0
|
||||
1736798400,3023.97,3103.38,3010.35,3090.15,177752432.0
|
||||
1736802000,3090.41,3118.78,3081.97,3112.72,165786786.0
|
||||
1736805600,3112.72,3141.66,3110.97,3130.64,116974672.0
|
||||
1736809200,3130.64,3139.43,3124.38,3136.15,47544220.0
|
||||
1736812800,3136.15,3147.25,3124.06,3132.57,84185166.0
|
||||
1736816400,3132.57,3157.97,3132.19,3138.01,94074596.0
|
||||
1736820000,3137.9,3165.79,3130.36,3155.98,85221592.0
|
||||
1736823600,3155.98,3174.3,3153.45,3157.58,54405210.0
|
||||
1736827200,3157.58,3174.12,3157.58,3171.39,55857890.0
|
||||
1736830800,3171.23,3181.33,3158.29,3160.65,79933988.0
|
||||
1736834400,3160.65,3187.06,3156.26,3182.95,73104704.0
|
||||
1736838000,3182.95,3189.0,3169.86,3170.4,56505048.0
|
||||
1736841600,3170.4,3191.27,3168.36,3181.91,74257186.0
|
||||
1736845200,3182.0,3255.21,3176.59,3242.42,259501370.0
|
||||
1736848800,3242.42,3242.89,3219.7,3233.75,124964948.0
|
||||
1736852400,3233.75,3234.23,3209.7,3216.28,67811012.0
|
||||
1736856000,3216.12,3218.73,3179.51,3182.39,163014218.0
|
||||
1736859600,3182.5,3231.74,3178.0,3207.21,199494486.0
|
||||
1736863200,3207.22,3232.31,3186.1,3190.41,130742936.0
|
||||
1736866800,3190.94,3214.98,3186.11,3197.86,137337984.0
|
||||
1736870400,3197.86,3202.94,3171.03,3189.99,137636112.0
|
||||
1736874000,3189.99,3211.7,3182.72,3211.33,80978048.0
|
||||
1736877600,3211.1,3231.4,3205.89,3223.37,92417500.0
|
||||
1736881200,3223.37,3234.31,3211.09,3212.9,62213378.0
|
||||
1736884800,3212.9,3224.69,3193.52,3221.24,82723252.0
|
||||
1736888400,3221.24,3232.5,3211.25,3214.72,50505536.0
|
||||
1736892000,3214.72,3239.2,3212.11,3233.81,35697584.0
|
||||
1736895600,3233.81,3236.6,3221.18,3223.99,28458870.0
|
||||
1736899200,3223.99,3241.26,3216.09,3220.18,62474718.0
|
||||
1736902800,3220.18,3244.58,3215.73,3228.96,71656158.0
|
||||
1736906400,3228.96,3238.69,3205.0,3211.66,70425488.0
|
||||
1736910000,3211.77,3240.66,3207.26,3239.89,54759212.0
|
||||
1736913600,3239.88,3251.11,3220.79,3225.01,81473270.0
|
||||
1736917200,3224.99,3234.39,3213.86,3228.57,46760866.0
|
||||
1736920800,3228.57,3231.82,3215.17,3229.14,47369732.0
|
||||
1736924400,3229.14,3249.69,3228.61,3231.31,72666604.0
|
||||
1736928000,3231.31,3246.86,3228.52,3230.98,60786620.0
|
||||
1736931600,3231.1,3236.52,3195.34,3199.21,97527064.0
|
||||
1736935200,3199.21,3217.14,3196.79,3208.08,62126134.0
|
||||
1736938800,3208.08,3208.96,3185.53,3198.3,77459828.0
|
||||
1736942400,3198.3,3210.72,3185.4,3200.66,78921674.0
|
||||
1736946000,3200.66,3297.25,3200.4,3297.01,361680060.0
|
||||
1736949600,3297.01,3330.61,3274.57,3328.32,260514000.0
|
||||
1736953200,3328.37,3353.09,3324.53,3333.17,216457060.0
|
||||
1736956800,3333.07,3365.41,3332.56,3338.3,139245078.0
|
||||
1736960400,3338.3,3357.46,3322.99,3324.68,90410118.0
|
||||
1736964000,3324.68,3373.77,3324.68,3370.89,81676352.0
|
||||
1736967600,3370.8,3442.18,3366.66,3433.76,263310966.0
|
||||
1736971200,3433.76,3472.38,3431.66,3432.83,208944138.0
|
||||
1736974800,3432.81,3445.86,3426.0,3431.39,62340338.0
|
||||
1736978400,3431.39,3434.99,3417.53,3429.39,51207524.0
|
||||
1736982000,3429.48,3451.12,3419.23,3449.82,66010384.0
|
||||
1736985600,3449.82,3458.48,3385.69,3397.95,171382692.0
|
||||
1736989200,3397.95,3412.55,3391.65,3402.58,46722624.0
|
||||
1736992800,3402.58,3405.0,3380.39,3384.03,61431958.0
|
||||
1736996400,3384.03,3386.8,3345.74,3362.11,132369822.0
|
||||
1737000000,3362.11,3374.53,3360.45,3373.24,55905782.0
|
||||
1737003600,3373.24,3376.07,3364.58,3366.9,27209092.0
|
||||
1737007200,3366.9,3386.02,3361.78,3378.89,55504360.0
|
||||
1737010800,3378.86,3389.29,3375.89,3382.41,42026166.0
|
||||
1737014400,3382.41,3384.0,3301.88,3315.42,213550818.0
|
||||
1737018000,3315.44,3335.0,3298.01,3334.8,173270654.0
|
||||
1737021600,3334.8,3340.99,3328.0,3332.2,59671834.0
|
||||
1737025200,3332.87,3354.87,3320.07,3349.64,111186100.0
|
||||
1737028800,3349.65,3363.28,3340.11,3355.43,97407238.0
|
||||
1737032400,3355.53,3357.3,3309.0,3327.07,167514778.0
|
||||
1737036000,3327.1,3341.53,3264.28,3276.1,285205966.0
|
||||
1737039600,3276.25,3332.09,3268.53,3331.98,228397622.0
|
||||
1737043200,3331.98,3360.66,3329.4,3339.22,152861984.0
|
||||
1737046800,3338.77,3364.41,3329.91,3342.69,93468544.0
|
||||
1737050400,3342.69,3349.79,3308.93,3324.61,90234800.0
|
||||
1737054000,3324.61,3344.94,3318.01,3343.35,43458370.0
|
||||
1737057600,3343.35,3351.99,3324.68,3335.77,53289036.0
|
||||
1737061200,3335.71,3336.78,3310.16,3319.09,54644580.0
|
||||
1737064800,3319.09,3319.31,3267.03,3296.7,131717852.0
|
||||
1737068400,3296.7,3311.39,3289.02,3306.93,68247846.0
|
||||
1737072000,3306.93,3316.78,3306.59,3312.78,42675412.0
|
||||
1737075600,3312.78,3393.76,3312.72,3384.73,214952370.0
|
||||
1737079200,3384.73,3396.23,3354.05,3372.07,140006948.0
|
||||
1737082800,3372.07,3376.06,3357.4,3359.98,47167210.0
|
||||
1737086400,3359.98,3371.42,3346.44,3369.69,64118896.0
|
||||
1737090000,3369.69,3374.77,3358.19,3365.51,44641394.0
|
||||
1737093600,3365.51,3374.7,3363.39,3366.38,39763556.0
|
||||
1737097200,3366.38,3393.91,3361.3,3371.36,70940042.0
|
||||
1737100800,3371.36,3412.78,3371.0,3405.9,121892422.0
|
||||
1737104400,3405.9,3413.03,3399.07,3402.51,70929662.0
|
||||
1737108000,3402.51,3437.78,3401.1,3426.64,102788236.0
|
||||
1737111600,3426.64,3429.5,3415.53,3422.1,55018198.0
|
||||
1737115200,3422.1,3425.46,3399.89,3406.78,90512196.0
|
||||
1737118800,3406.78,3422.93,3394.96,3400.81,77679768.0
|
||||
1737122400,3400.76,3431.89,3400.75,3427.7,167275558.0
|
||||
1737126000,3427.49,3449.69,3403.1,3420.33,195084666.0
|
||||
1737129600,3420.32,3447.59,3410.53,3436.43,111256436.0
|
||||
1737133200,3436.43,3441.9,3406.02,3413.26,100510326.0
|
||||
1737136800,3413.38,3428.83,3409.07,3410.49,47213594.0
|
||||
1737140400,3410.49,3443.03,3409.73,3430.79,91962986.0
|
||||
1737144000,3431.02,3524.4,3424.26,3514.04,286751750.0
|
||||
1737147600,3514.04,3524.53,3465.44,3472.64,136440486.0
|
||||
1737151200,3472.64,3486.56,3466.01,3468.91,61781218.0
|
||||
1737154800,3468.91,3483.87,3461.49,3472.39,47196678.0
|
||||
1737158400,3472.39,3492.94,3467.34,3485.83,52615116.0
|
||||
1737162000,3485.83,3485.83,3453.85,3464.95,63843980.0
|
||||
1737165600,3464.95,3473.28,3446.9,3450.06,47257378.0
|
||||
1737169200,3450.11,3455.0,3366.79,3382.77,229697520.0
|
||||
1737172800,3382.77,3394.52,3350.01,3364.52,172058940.0
|
||||
1737176400,3364.52,3369.99,3290.01,3292.89,274237194.0
|
||||
1737180000,3292.89,3339.39,3291.66,3312.76,153000770.0
|
||||
1737183600,3312.76,3314.37,3280.01,3286.46,147604794.0
|
||||
1737187200,3286.47,3294.66,3251.0,3291.85,228254978.0
|
||||
1737190800,3291.85,3299.87,3252.31,3269.83,127768426.0
|
||||
1737194400,3269.89,3276.99,3225.14,3265.78,252550352.0
|
||||
1737198000,3265.81,3323.48,3247.76,3310.18,227271112.0
|
||||
1737201600,3310.29,3320.91,3273.3,3288.68,159370596.0
|
||||
1737205200,3288.68,3322.36,3278.33,3321.84,137949644.0
|
||||
1737208800,3321.84,3325.22,3287.76,3295.86,97189442.0
|
||||
1737212400,3295.86,3346.2,3283.95,3305.77,219267024.0
|
||||
1737216000,3305.89,3315.72,3252.51,3252.51,243473644.0
|
||||
1737219600,3252.51,3284.8,3252.12,3257.2,97599068.0
|
||||
1737223200,3256.5,3283.03,3249.0,3263.66,83528110.0
|
||||
1737226800,3263.54,3292.49,3260.39,3289.08,56544316.0
|
||||
1737230400,3289.08,3290.88,3256.74,3264.87,58021088.0
|
||||
1737234000,3264.88,3285.06,3262.08,3273.72,37319280.0
|
||||
1737237600,3273.72,3287.67,3270.02,3279.36,30291248.0
|
||||
1737241200,3279.42,3320.34,3278.22,3306.69,83215168.0
|
||||
1737244800,3306.69,3322.38,3297.72,3315.68,66796494.0
|
||||
1737248400,3315.68,3365.18,3312.9,3352.57,155449194.0
|
||||
1737252000,3352.57,3377.32,3346.9,3352.47,82240816.0
|
||||
1737255600,3352.47,3365.32,3332.52,3334.97,97009222.0
|
||||
1737259200,3334.97,3337.32,3276.02,3282.51,202282652.0
|
||||
1737262800,3282.51,3299.78,3258.18,3298.57,105321972.0
|
||||
1737266400,3298.57,3299.93,3280.91,3282.12,47257870.0
|
||||
1737270000,3282.18,3292.37,3261.31,3275.02,88322326.0
|
||||
1737273600,3275.02,3277.15,3215.0,3221.19,203332442.0
|
||||
1737277200,3221.19,3233.18,3143.11,3226.34,411242284.0
|
||||
1737280800,3225.66,3226.18,3173.02,3174.2,166786984.0
|
||||
1737284400,3174.3,3188.53,3131.0,3149.56,209689408.0
|
||||
1737288000,3150.33,3210.59,3144.26,3205.26,164299906.0
|
||||
1737291600,3205.26,3277.31,3193.14,3273.7,251270350.0
|
||||
1737295200,3275.24,3395.99,3258.42,3380.19,863763538.0
|
||||
1737298800,3380.11,3424.81,3352.82,3383.79,333831610.0
|
||||
1737302400,3383.77,3424.04,3377.57,3410.83,168812328.0
|
||||
1737306000,3410.88,3448.14,3385.96,3442.35,194255198.0
|
||||
1737309600,3442.35,3449.0,3406.9,3434.0,106325700.0
|
||||
1737313200,3433.4,3434.0,3407.59,3419.94,57312166.0
|
||||
1737316800,3419.94,3432.95,3351.04,3378.22,144020792.0
|
||||
1737320400,3378.22,3379.4,3207.05,3235.01,501866746.0
|
||||
1737324000,3232.5,3311.84,3156.18,3265.77,478996130.0
|
||||
1737327600,3265.76,3265.76,3160.07,3213.52,466762210.0
|
||||
1737331200,3214.05,3225.31,3142.73,3170.28,407986910.0
|
||||
1737334800,3170.27,3219.48,3165.19,3200.27,229060286.0
|
||||
1737338400,3199.96,3273.76,3196.01,3243.54,218587436.0
|
||||
1737342000,3244.04,3279.99,3227.01,3271.39,119161842.0
|
||||
1737345600,3271.35,3298.81,3249.6,3269.87,100450292.0
|
||||
1737349200,3269.87,3318.14,3266.79,3290.91,124554022.0
|
||||
1737352800,3290.91,3454.72,3285.41,3417.53,388228564.0
|
||||
1737356400,3417.41,3441.9,3351.89,3383.02,374577926.0
|
||||
1737360000,3382.95,3412.04,3380.02,3392.15,127096766.0
|
||||
1737363600,3392.15,3411.93,3352.99,3364.15,186871672.0
|
||||
1737367200,3364.15,3391.99,3341.78,3354.39,126612530.0
|
||||
1737370800,3354.39,3388.33,3334.21,3375.69,172373108.0
|
||||
1737374400,3375.69,3379.6,3256.4,3303.82,367009686.0
|
||||
1737378000,3303.93,3354.87,3295.01,3342.28,210560350.0
|
||||
1737381600,3342.23,3361.6,3329.61,3358.56,154930784.0
|
||||
1737385200,3358.55,3378.98,3315.21,3335.57,241523322.0
|
||||
1737388800,3335.93,3390.0,3276.81,3380.16,274158310.0
|
||||
1737392400,3379.9,3384.07,3205.0,3292.24,795072372.0
|
||||
1737396000,3292.24,3341.58,3283.72,3330.16,213855650.0
|
||||
1737399600,3330.04,3364.11,3308.4,3342.92,128630890.0
|
||||
1737403200,3342.92,3344.32,3313.98,3320.27,77237838.0
|
||||
1737406800,3320.27,3337.16,3280.51,3284.42,82249858.0
|
||||
1737410400,3284.36,3324.16,3255.28,3318.25,103096784.0
|
||||
1737414000,3318.14,3320.28,3260.59,3283.21,98151492.0
|
||||
1737417600,3283.21,3289.47,3220.47,3225.66,167574272.0
|
||||
1737421200,3225.9,3260.77,3203.89,3250.4,181075574.0
|
||||
1737424800,3250.4,3272.75,3227.81,3256.73,97124780.0
|
||||
1737428400,3256.73,3269.67,3248.01,3262.23,63734320.0
|
||||
1737432000,3262.23,3262.25,3212.81,3246.37,95284392.0
|
||||
1737435600,3246.37,3253.9,3216.03,3224.39,73892594.0
|
||||
1737439200,3224.65,3253.97,3212.82,3241.15,98098922.0
|
||||
1737442800,3241.15,3266.0,3234.55,3254.01,67830494.0
|
||||
1737446400,3254.0,3262.97,3233.55,3253.27,67584610.0
|
||||
1737450000,3253.27,3276.4,3249.82,3275.74,77660896.0
|
||||
1737453600,3275.74,3308.8,3270.41,3302.06,104765974.0
|
||||
1737457200,3302.06,3313.19,3291.81,3304.39,91283102.0
|
||||
1737460800,3304.39,3325.85,3293.07,3306.62,128372258.0
|
||||
1737464400,3306.62,3314.49,3288.96,3308.85,80976134.0
|
||||
1737468000,3308.71,3333.73,3279.0,3288.51,211256600.0
|
||||
1737471600,3288.51,3300.99,3261.77,3294.1,128559286.0
|
||||
1737475200,3294.1,3349.14,3289.36,3313.34,197211854.0
|
||||
1737478800,3313.15,3356.04,3307.09,3350.37,90531198.0
|
||||
1737482400,3350.37,3367.88,3329.87,3345.17,100399176.0
|
||||
1737486000,3345.18,3346.29,3317.24,3331.74,80784936.0
|
||||
1737489600,3331.74,3339.63,3303.34,3313.41,70082146.0
|
||||
1737493200,3313.43,3335.71,3301.87,3331.61,54339844.0
|
||||
1737496800,3331.58,3346.58,3326.0,3333.06,38895058.0
|
||||
1737500400,3333.91,3334.32,3307.27,3326.74,73269766.0
|
||||
1737504000,3326.74,3365.96,3322.53,3361.54,73640766.0
|
||||
1737507600,3361.54,3361.83,3321.61,3328.53,68953758.0
|
||||
1737511200,3328.34,3346.78,3322.0,3343.32,51151860.0
|
||||
1737514800,3343.32,3347.85,3327.2,3330.54,33271698.0
|
||||
1737518400,3330.54,3332.93,3315.84,3331.15,33810516.0
|
||||
1737522000,3331.15,3339.33,3317.01,3317.66,29934040.0
|
||||
1737525600,3317.66,3323.48,3301.61,3308.82,50870486.0
|
||||
1737529200,3308.82,3310.36,3286.18,3287.05,76036546.0
|
||||
1737532800,3286.97,3300.02,3273.0,3298.39,92349262.0
|
||||
1737536400,3298.39,3303.49,3289.1,3297.4,35948534.0
|
||||
1737540000,3297.4,3307.4,3287.82,3306.99,40550278.0
|
||||
1737543600,3306.99,3328.83,3303.3,3325.53,53272540.0
|
||||
1737547200,3325.53,3331.45,3310.14,3315.9,52994384.0
|
||||
1737550800,3315.85,3318.31,3279.25,3283.32,100473366.0
|
||||
1737554400,3283.39,3310.99,3266.7,3292.02,165657884.0
|
||||
1737558000,3292.72,3304.69,3268.95,3277.5,108866836.0
|
||||
1737561600,3277.5,3282.91,3261.0,3277.1,97706330.0
|
||||
1737565200,3277.15,3287.01,3271.02,3284.6,48476310.0
|
||||
1737568800,3284.6,3290.85,3239.08,3248.93,96991712.0
|
||||
1737572400,3249.38,3279.0,3247.52,3261.51,73598538.0
|
||||
1737576000,3261.51,3276.21,3253.01,3255.86,46359000.0
|
||||
1737579600,3255.82,3264.32,3240.0,3258.42,64803018.0
|
||||
1737583200,3258.42,3265.69,3231.89,3237.63,52779412.0
|
||||
1737586800,3237.63,3245.99,3221.53,3241.77,78166668.0
|
||||
1737590400,3241.77,3260.81,3237.04,3252.92,56714310.0
|
||||
1737594000,3252.92,3257.59,3220.0,3234.28,65753656.0
|
||||
1737597600,3234.34,3236.57,3203.23,3222.26,94598842.0
|
||||
1737601200,3222.48,3242.35,3214.38,3225.64,57395280.0
|
||||
1737604800,3225.71,3230.89,3182.66,3196.01,114327136.0
|
||||
1737608400,3196.01,3220.5,3195.34,3213.89,55114374.0
|
||||
1737612000,3213.89,3223.98,3203.26,3207.1,39191160.0
|
||||
1737615600,3207.1,3232.44,3197.39,3231.96,53561668.0
|
||||
1737619200,3231.96,3232.44,3207.44,3209.22,45550378.0
|
||||
1737622800,3209.26,3221.14,3191.13,3195.94,60758744.0
|
||||
1737626400,3195.94,3212.68,3187.34,3203.3,65237070.0
|
||||
1737630000,3203.3,3216.15,3194.82,3212.78,49857888.0
|
||||
1737633600,3212.78,3217.94,3188.52,3202.62,78595334.0
|
||||
1737637200,3202.62,3242.4,3201.12,3233.01,108985424.0
|
||||
1737640800,3232.89,3285.36,3221.81,3274.6,249651856.0
|
||||
1737644400,3274.6,3280.91,3225.01,3258.51,281277612.0
|
||||
1737648000,3258.51,3296.73,3252.0,3253.65,190993782.0
|
||||
1737651600,3253.64,3270.76,3230.02,3264.89,108032146.0
|
||||
1737655200,3264.92,3269.43,3234.0,3239.9,70643910.0
|
||||
1737658800,3239.9,3245.0,3193.09,3203.76,157291960.0
|
||||
1737662400,3203.71,3294.1,3195.86,3246.15,442292904.0
|
||||
1737666000,3246.11,3255.52,3212.77,3247.4,120420326.0
|
||||
1737669600,3247.85,3322.98,3245.19,3321.94,160218202.0
|
||||
1737673200,3321.66,3347.53,3311.27,3337.88,118336168.0
|
||||
1737676800,3337.88,3348.99,3284.11,3284.72,117342552.0
|
||||
1737680400,3284.69,3325.37,3283.51,3297.47,90561650.0
|
||||
1737684000,3297.49,3313.1,3278.58,3284.64,67232158.0
|
||||
1737687600,3285.21,3311.93,3275.0,3301.41,91279686.0
|
||||
1737691200,3301.35,3339.92,3291.13,3332.72,87852684.0
|
||||
1737694800,3332.45,3409.47,3328.55,3409.22,199536164.0
|
||||
1737698400,3409.28,3420.61,3378.01,3385.27,167874148.0
|
||||
1737702000,3385.32,3400.36,3371.39,3380.59,82619272.0
|
||||
1737705600,3380.59,3415.57,3374.6,3400.57,104233816.0
|
||||
1737709200,3400.58,3412.03,3391.01,3400.87,62308630.0
|
||||
1737712800,3400.73,3414.21,3390.53,3412.88,57566642.0
|
||||
1737716400,3412.88,3413.9,3391.8,3398.69,44487372.0
|
||||
1737720000,3398.69,3404.63,3385.41,3403.48,58305470.0
|
||||
1737723600,3403.48,3429.0,3390.25,3394.44,113839350.0
|
||||
1737727200,3394.44,3421.99,3378.04,3398.31,143869564.0
|
||||
1737730800,3398.34,3408.99,3355.0,3382.62,182652482.0
|
||||
1737734400,3382.62,3397.06,3375.88,3379.29,76641652.0
|
||||
1737738000,3379.35,3405.0,3371.76,3396.65,80638944.0
|
||||
1737741600,3396.6,3408.37,3373.79,3380.06,61571938.0
|
||||
1737745200,3380.0,3393.91,3363.16,3367.16,95047310.0
|
||||
1737748800,3367.19,3377.48,3325.47,3327.04,158225220.0
|
||||
1737752400,3327.75,3345.6,3322.22,3328.33,70830146.0
|
||||
1737756000,3328.59,3335.82,3321.01,3324.47,47898010.0
|
||||
1737759600,3324.47,3324.72,3302.5,3309.02,79667410.0
|
||||
1737763200,3309.02,3312.97,3268.32,3272.36,114215218.0
|
||||
1737766800,3272.27,3304.88,3267.35,3292.74,75981114.0
|
||||
1737770400,3292.74,3303.18,3287.86,3293.48,31908786.0
|
||||
1737774000,3293.48,3322.77,3290.52,3318.01,62089274.0
|
||||
1737777600,3318.01,3318.57,3296.69,3298.78,41619704.0
|
||||
1737781200,3298.78,3301.7,3286.6,3295.01,41837638.0
|
||||
1737784800,3295.01,3302.39,3282.57,3290.84,42042506.0
|
||||
1737788400,3290.78,3294.18,3281.83,3289.78,31177086.0
|
||||
1737792000,3289.78,3299.99,3281.43,3289.6,41350052.0
|
||||
1737795600,3289.6,3291.48,3280.0,3282.3,25686474.0
|
||||
1737799200,3282.25,3296.12,3270.4,3294.2,64393426.0
|
||||
1737802800,3294.24,3310.53,3289.62,3305.5,56540872.0
|
||||
1737806400,3305.5,3306.2,3293.02,3304.78,37528022.0
|
||||
1737810000,3304.78,3309.07,3299.01,3306.3,28691106.0
|
||||
1737813600,3306.3,3318.59,3297.0,3314.36,36664870.0
|
||||
1737817200,3314.41,3343.6,3308.42,3333.02,126783138.0
|
||||
1737820800,3332.94,3347.91,3328.7,3341.0,60067516.0
|
||||
1737824400,3341.08,3344.93,3329.09,3343.34,50834736.0
|
||||
1737828000,3343.34,3347.11,3334.0,3340.79,28862350.0
|
||||
1737831600,3340.79,3342.99,3333.01,3338.71,18958684.0
|
||||
1737835200,3338.71,3349.46,3334.15,3335.62,26919424.0
|
||||
1737838800,3335.66,3341.62,3330.89,3338.49,22254066.0
|
||||
1737842400,3338.49,3344.78,3330.72,3332.85,16937198.0
|
||||
1737846000,3332.85,3336.13,3313.36,3317.9,53735534.0
|
||||
1737849600,3317.9,3326.9,3312.38,3319.74,34181402.0
|
||||
1737853200,3319.74,3336.98,3317.18,3330.23,29997596.0
|
||||
1737856800,3330.16,3330.16,3316.39,3327.22,26861986.0
|
||||
1737860400,3327.22,3349.05,3324.99,3337.47,39360420.0
|
||||
1737864000,3337.47,3364.99,3335.0,3350.99,60812656.0
|
||||
1737867600,3350.99,3352.29,3336.82,3341.07,29644874.0
|
||||
1737871200,3341.07,3346.39,3337.0,3340.36,20394300.0
|
||||
1737874800,3340.36,3344.23,3328.67,3337.56,24837804.0
|
||||
1737878400,3337.56,3342.4,3322.11,3330.52,40964058.0
|
||||
1737882000,3330.52,3331.44,3291.46,3297.06,116578472.0
|
||||
1737885600,3297.06,3305.58,3294.6,3304.27,37585964.0
|
||||
1737889200,3304.27,3314.83,3301.35,3310.45,41215532.0
|
||||
1737892800,3310.45,3310.46,3298.41,3306.64,30648776.0
|
||||
1737896400,3306.47,3308.32,3299.05,3306.62,41653970.0
|
||||
1737900000,3306.62,3311.91,3295.61,3309.48,35483260.0
|
||||
1737903600,3309.48,3319.49,3307.06,3313.61,37547550.0
|
||||
1737907200,3313.61,3318.63,3306.37,3314.39,35922004.0
|
||||
1737910800,3314.39,3343.45,3313.81,3336.32,70866966.0
|
||||
1737914400,3336.32,3341.72,3332.34,3335.83,51959942.0
|
||||
1737918000,3335.83,3340.69,3328.77,3337.51,32405448.0
|
||||
1737921600,3337.51,3341.35,3325.55,3328.26,20768510.0
|
||||
1737925200,3328.3,3328.66,3290.68,3295.01,77584126.0
|
||||
1737928800,3294.88,3308.55,3280.01,3286.36,69877756.0
|
||||
1737932400,3286.31,3286.31,3227.47,3230.78,274026736.0
|
||||
1737936000,3230.65,3252.53,3208.01,3212.62,180964884.0
|
||||
1737939600,3212.63,3223.16,3186.86,3194.14,165658226.0
|
||||
1737943200,3193.87,3194.0,3162.32,3169.45,191214386.0
|
||||
1737946800,3169.45,3197.46,3160.24,3182.11,92065924.0
|
||||
1737950400,3182.11,3182.11,3152.65,3155.73,87218334.0
|
||||
1737954000,3155.73,3163.38,3113.02,3139.75,203842944.0
|
||||
1737957600,3139.8,3148.9,3077.81,3082.76,298234274.0
|
||||
1737961200,3082.76,3090.27,3020.93,3069.26,415672338.0
|
||||
1737964800,3069.11,3071.81,3037.23,3064.19,162267474.0
|
||||
1737968400,3064.24,3088.9,3056.92,3079.48,119649952.0
|
||||
1737972000,3079.48,3080.18,3032.7,3037.11,115805844.0
|
||||
1737975600,3037.05,3074.22,3036.34,3057.48,90659514.0
|
||||
1737979200,3057.48,3121.61,3052.14,3101.53,187794412.0
|
||||
1737982800,3101.47,3133.28,3087.85,3097.86,231867426.0
|
||||
1737986400,3097.86,3144.83,3081.86,3136.15,203057890.0
|
||||
1737990000,3135.72,3150.8,3110.7,3131.09,147629814.0
|
||||
1737993600,3131.97,3132.04,3074.43,3083.0,203328436.0
|
||||
1737997200,3082.95,3098.11,3065.24,3071.53,114566258.0
|
||||
1738000800,3071.57,3087.79,3045.3,3053.88,103725792.0
|
||||
1738004400,3053.91,3080.93,3053.91,3074.89,83541122.0
|
||||
1738008000,3074.89,3147.82,3071.83,3145.22,143522924.0
|
||||
1738011600,3143.97,3237.99,3141.23,3158.16,299739690.0
|
||||
1738015200,3158.16,3180.06,3145.0,3169.25,79707278.0
|
||||
1738018800,3169.25,3181.44,3158.71,3180.75,56318916.0
|
||||
1738022400,3180.79,3202.51,3163.01,3166.22,77481438.0
|
||||
1738026000,3166.27,3175.57,3150.31,3165.28,59624336.0
|
||||
1738029600,3164.96,3198.41,3164.14,3191.6,59293696.0
|
||||
1738033200,3191.56,3216.86,3174.1,3201.47,109284848.0
|
||||
1738036800,3201.47,3221.53,3196.11,3210.64,83876044.0
|
||||
1738040400,3210.67,3220.69,3207.19,3211.69,44259070.0
|
||||
1738044000,3211.69,3214.02,3190.44,3193.55,48991426.0
|
||||
1738047600,3193.43,3214.7,3187.51,3194.62,50038618.0
|
||||
1738051200,3194.62,3204.1,3182.01,3197.47,81225676.0
|
||||
1738054800,3197.42,3200.58,3183.0,3195.72,38138356.0
|
||||
1738058400,3195.72,3206.32,3187.84,3192.87,39941280.0
|
||||
1738062000,3192.87,3199.56,3175.47,3185.2,39874048.0
|
||||
1738065600,3185.2,3194.62,3165.52,3173.72,60379984.0
|
||||
1738069200,3173.64,3188.0,3169.01,3174.14,39825956.0
|
||||
1738072800,3174.14,3179.53,3156.0,3171.09,125274148.0
|
||||
1738076400,3171.34,3213.44,3169.55,3180.15,145496580.0
|
||||
1738080000,3180.15,3181.81,3130.72,3149.59,141098534.0
|
||||
1738083600,3149.46,3181.56,3149.05,3166.11,107192478.0
|
||||
1738087200,3166.07,3176.76,3139.18,3142.6,67871000.0
|
||||
1738090800,3142.6,3149.32,3118.98,3146.4,100694056.0
|
||||
1738094400,3146.48,3153.45,3092.03,3094.31,134835852.0
|
||||
1738098000,3093.42,3113.6,3051.32,3052.43,169849482.0
|
||||
1738101600,3052.32,3089.94,3038.2,3082.86,165296122.0
|
||||
1738105200,3082.93,3095.01,3052.51,3075.96,82801056.0
|
||||
1738108800,3075.96,3116.55,3075.12,3108.9,1274170.0
|
||||
1738112400,3108.85,3119.0,3097.82,3109.31,1144288.0
|
||||
1738116000,3109.31,3128.3,3108.69,3112.81,4755046.0
|
||||
1738119600,3112.74,3131.26,3106.65,3129.02,44437084.0
|
||||
1738123200,3129.04,3138.89,3121.51,3122.94,51333698.0
|
||||
1738126800,3122.87,3133.32,3115.46,3129.66,45341342.0
|
||||
1738130400,3129.66,3148.96,3127.16,3145.88,60770492.0
|
||||
1738134000,3145.88,3158.0,3137.4,3155.64,54759780.0
|
||||
1738137600,3155.64,3162.68,3143.0,3146.4,48938372.0
|
||||
1738141200,3146.4,3147.23,3121.93,3129.27,68754366.0
|
||||
1738144800,3129.27,3142.05,3119.65,3136.48,46871668.0
|
||||
1738148400,3136.48,3142.08,3130.31,3133.64,37915812.0
|
||||
1738152000,3133.64,3138.84,3112.15,3117.69,73520948.0
|
||||
1738155600,3117.69,3120.73,3087.77,3100.83,123395826.0
|
||||
1738159200,3100.81,3115.17,3079.41,3095.1,130122046.0
|
||||
1738162800,3095.81,3117.95,3092.7,3099.65,80143028.0
|
||||
|
@@ -1,172 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Bitmart 多周期K线数据抓取 - 供回测与实盘使用
|
||||
与 bitmart/回测.py、bitmart/抓取多周期K线.py 相同的 API 调用方式
|
||||
"""
|
||||
|
||||
import time
|
||||
import csv
|
||||
import os
|
||||
from typing import List, Dict, Optional
|
||||
try:
|
||||
from loguru import logger
|
||||
except ImportError:
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# 项目根目录为 lm_code,bitmart 包在根目录
|
||||
import sys
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
ROOT_DIR = os.path.dirname(SCRIPT_DIR)
|
||||
if ROOT_DIR not in sys.path:
|
||||
sys.path.insert(0, ROOT_DIR)
|
||||
|
||||
# 默认 API(与 bitmart/回测.py 一致,回测仅拉数据可不填有效 key)
|
||||
# APIContract 在 fetch_klines / fetch_multi_timeframe 内按需导入,以便 --no-api 回测不依赖 bitmart
|
||||
DEFAULT_API_KEY = "a0fb7b98464fd9bcce67e7c519d58ec10d0c38a8"
|
||||
DEFAULT_SECRET_KEY = "4eaeba78e77aeaab1c2027f846a276d164f264a44c2c1bb1c5f3be50c8de1ca5"
|
||||
DEFAULT_MEMO = "合约交易"
|
||||
CONTRACT_SYMBOL = "ETHUSDT"
|
||||
|
||||
|
||||
def _format_bar(k: dict) -> dict:
|
||||
"""API 单根K线 -> 统一格式,id 为秒时间戳"""
|
||||
return {
|
||||
"id": int(k["timestamp"]),
|
||||
"open": float(k["open_price"]),
|
||||
"high": float(k["high_price"]),
|
||||
"low": float(k["low_price"]),
|
||||
"close": float(k["close_price"]),
|
||||
"volume": float(k.get("volume", 0)),
|
||||
}
|
||||
|
||||
|
||||
def fetch_klines(
|
||||
contract_api: "APIContract",
|
||||
step: int,
|
||||
start_time: int,
|
||||
end_time: int,
|
||||
symbol: str = CONTRACT_SYMBOL,
|
||||
) -> List[Dict]:
|
||||
"""
|
||||
拉取指定周期的K线(与 bitmart 回测抓取方式一致)。
|
||||
:param contract_api: bitmart.api_contract.APIContract 实例
|
||||
:param step: K线周期(分钟),如 5/15/60
|
||||
:param start_time: 开始时间戳(秒)
|
||||
:param end_time: 结束时间戳(秒)
|
||||
:param symbol: 合约符号
|
||||
:return: [{"id", "open", "high", "low", "close", "volume"}, ...],按 id 升序
|
||||
"""
|
||||
all_data: List[Dict] = []
|
||||
existing_ids = set()
|
||||
request_interval = step * 60 * 500 # 每次最多约 500 根
|
||||
current_start = start_time
|
||||
|
||||
while current_start < end_time:
|
||||
current_end = min(current_start + request_interval, end_time)
|
||||
try:
|
||||
response = contract_api.get_kline(
|
||||
contract_symbol=symbol,
|
||||
step=step,
|
||||
start_time=current_start,
|
||||
end_time=current_end,
|
||||
)[0]
|
||||
if response.get("code") != 1000:
|
||||
logger.warning(f"get_kline code={response.get('code')}, msg={response.get('msg')}")
|
||||
time.sleep(1)
|
||||
current_start = current_end
|
||||
continue
|
||||
data = response.get("data", [])
|
||||
except Exception as e:
|
||||
logger.warning(f"get_kline 异常 step={step} {e},60秒后重试")
|
||||
time.sleep(60)
|
||||
continue
|
||||
|
||||
for k in data:
|
||||
k_id = int(k["timestamp"])
|
||||
if k_id in existing_ids:
|
||||
continue
|
||||
existing_ids.add(k_id)
|
||||
all_data.append(_format_bar(k))
|
||||
|
||||
if len(data) < 500:
|
||||
current_start = current_end
|
||||
else:
|
||||
all_data.sort(key=lambda x: x["id"])
|
||||
current_start = all_data[-1]["id"] + 1
|
||||
|
||||
time.sleep(0.25)
|
||||
|
||||
all_data.sort(key=lambda x: x["id"])
|
||||
return all_data
|
||||
|
||||
|
||||
def fetch_multi_timeframe(
|
||||
start_time: int,
|
||||
end_time: int,
|
||||
steps: List[int] = None,
|
||||
api_key: str = DEFAULT_API_KEY,
|
||||
secret_key: str = DEFAULT_SECRET_KEY,
|
||||
memo: str = DEFAULT_MEMO,
|
||||
symbol: str = CONTRACT_SYMBOL,
|
||||
) -> Dict[int, List[Dict]]:
|
||||
"""
|
||||
拉取多周期K线(5/15/60),供回测使用。
|
||||
:return: { step: [kline_list] }
|
||||
"""
|
||||
steps = steps or [5, 15, 60]
|
||||
from bitmart.api_contract import APIContract
|
||||
api = APIContract(api_key, secret_key, memo, timeout=(5, 15))
|
||||
result = {}
|
||||
for step in steps:
|
||||
logger.info(f"抓取 {step} 分钟 K 线: {start_time} ~ {end_time}")
|
||||
result[step] = fetch_klines(api, step, start_time, end_time, symbol)
|
||||
logger.info(f" -> {len(result[step])} 条")
|
||||
return result
|
||||
|
||||
|
||||
def save_klines_csv(klines: List[Dict], path: str) -> None:
|
||||
"""将K线保存为 CSV(id, open, high, low, close, volume)"""
|
||||
if not klines:
|
||||
return
|
||||
cols = ["id", "open", "high", "low", "close", "volume"]
|
||||
with open(path, "w", newline="", encoding="utf-8") as f:
|
||||
w = csv.DictWriter(f, fieldnames=cols)
|
||||
w.writeheader()
|
||||
for row in klines:
|
||||
w.writerow({k: row.get(k) for k in cols})
|
||||
logger.info(f"已保存 {len(klines)} 条到 {path}")
|
||||
|
||||
|
||||
def load_klines_csv(path: str) -> List[Dict]:
|
||||
"""从 CSV 加载K线"""
|
||||
if not os.path.isfile(path):
|
||||
return []
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
r = csv.DictReader(f)
|
||||
rows = list(r)
|
||||
out = []
|
||||
for row in rows:
|
||||
out.append({
|
||||
"id": int(row["id"]),
|
||||
"open": float(row["open"]),
|
||||
"high": float(row["high"]),
|
||||
"low": float(row["low"]),
|
||||
"close": float(row["close"]),
|
||||
"volume": float(row.get("volume", 0)),
|
||||
})
|
||||
out.sort(key=lambda x: x["id"])
|
||||
return out
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import datetime
|
||||
# 示例:拉取最近约 30 天 5/15/60 分钟数据并保存
|
||||
end_ts = int(time.time())
|
||||
start_ts = end_ts - 30 * 24 * 3600
|
||||
data_dir = os.path.join(SCRIPT_DIR, "data")
|
||||
os.makedirs(data_dir, exist_ok=True)
|
||||
data = fetch_multi_timeframe(start_ts, end_ts, steps=[5, 15, 60])
|
||||
for step, klines in data.items():
|
||||
path = os.path.join(data_dir, f"kline_{step}m.csv")
|
||||
save_klines_csv(klines, path)
|
||||
@@ -1,86 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
技术指标:EMA、ATR
|
||||
"""
|
||||
|
||||
from typing import List, Dict, Optional
|
||||
|
||||
|
||||
def ema(series: List[float], period: int) -> List[Optional[float]]:
|
||||
"""EMA(period),前 period-1 个为 None"""
|
||||
if not series or period < 1:
|
||||
return []
|
||||
k = 2.0 / (period + 1)
|
||||
out: List[Optional[float]] = [None] * (period - 1)
|
||||
s = sum(series[:period])
|
||||
out.append(s / period)
|
||||
for i in range(period, len(series)):
|
||||
val = series[i] * k + out[-1] * (1 - k) # type: ignore
|
||||
out.append(val)
|
||||
return out
|
||||
|
||||
|
||||
def atr(high: List[float], low: List[float], close: List[float], period: int) -> List[Optional[float]]:
|
||||
"""ATR(period),前 period 个为 None"""
|
||||
n = len(close)
|
||||
if n < period + 1 or len(high) != n or len(low) != n:
|
||||
return [None] * n
|
||||
tr_list: List[float] = []
|
||||
for i in range(n):
|
||||
if i == 0:
|
||||
tr_list.append(high[0] - low[0])
|
||||
else:
|
||||
tr = max(
|
||||
high[i] - low[i],
|
||||
abs(high[i] - close[i - 1]),
|
||||
abs(low[i] - close[i - 1])
|
||||
)
|
||||
tr_list.append(tr)
|
||||
return ema(tr_list, period)
|
||||
|
||||
|
||||
def get_ema_atr_from_klines(klines: List[Dict], ema_period: int, atr_period: int
|
||||
) -> tuple:
|
||||
"""
|
||||
从K线列表计算收盘价 EMA 和 ATR。
|
||||
返回 (ema_list, atr_list),长度与 klines 一致。
|
||||
"""
|
||||
close = [float(k["close"]) for k in klines]
|
||||
high = [float(k["high"]) for k in klines]
|
||||
low = [float(k["low"]) for k in klines]
|
||||
ema_list = ema(close, ema_period)
|
||||
atr_list = atr(high, low, close, atr_period)
|
||||
return ema_list, atr_list
|
||||
|
||||
|
||||
def align_higher_tf_ema(
|
||||
klines_5m: List[Dict],
|
||||
klines_higher: List[Dict],
|
||||
ema_fast: int,
|
||||
ema_slow: int
|
||||
) -> List[Dict]:
|
||||
"""
|
||||
根据更高周期K线计算 EMA,并按 5 分钟时间对齐。
|
||||
返回列表长度与 klines_5m 一致,每项为 {"ema_fast": float, "ema_slow": float} 或 None。
|
||||
"""
|
||||
if not klines_higher or len(klines_higher) < ema_slow:
|
||||
return [{}] * len(klines_5m)
|
||||
close_hi = [float(k["close"]) for k in klines_higher]
|
||||
ema_f = ema(close_hi, ema_fast)
|
||||
ema_s = ema(close_hi, ema_slow)
|
||||
id_hi = [k["id"] for k in klines_higher]
|
||||
result = []
|
||||
for k5 in klines_5m:
|
||||
t = k5["id"]
|
||||
# 当前 5m 时刻所属的更高周期:取 <= t 的最后一根
|
||||
idx = -1
|
||||
for i, tid in enumerate(id_hi):
|
||||
if tid <= t:
|
||||
idx = i
|
||||
else:
|
||||
break
|
||||
if idx >= ema_slow - 1 and ema_f[idx] is not None and ema_s[idx] is not None:
|
||||
result.append({"ema_fast": ema_f[idx], "ema_slow": ema_s[idx]})
|
||||
else:
|
||||
result.append({})
|
||||
return result
|
||||
@@ -1,305 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
自适应三分位趋势策略 - 核心逻辑
|
||||
趋势过滤、动态阈值、信号确认、市场状态
|
||||
"""
|
||||
|
||||
from typing import List, Dict, Optional, Tuple
|
||||
import sys
|
||||
import os
|
||||
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
if ROOT_DIR not in sys.path:
|
||||
sys.path.insert(0, ROOT_DIR)
|
||||
|
||||
from adaptive_third_strategy.config import (
|
||||
MIN_BODY_ATR_RATIO,
|
||||
MIN_VOLATILITY_PERCENT,
|
||||
EMA_SHORT,
|
||||
EMA_LONG_FAST,
|
||||
EMA_LONG_SLOW,
|
||||
EMA_MID_FAST,
|
||||
EMA_MID_SLOW,
|
||||
ATR_PERIOD,
|
||||
VOLATILITY_COEF_CLAMP,
|
||||
BASE_COEF,
|
||||
TREND_FAVOR_COEF,
|
||||
TREND_AGAINST_COEF,
|
||||
TREND_MODE,
|
||||
CONFIRM_REQUIRED,
|
||||
VOLUME_MA_PERIOD,
|
||||
VOLUME_RATIO_THRESHOLD,
|
||||
REVERSE_BREAK_MULT,
|
||||
MIN_BARS_SINCE_ENTRY,
|
||||
FORBIDDEN_PERIODS,
|
||||
ATR_PAUSE_MULT,
|
||||
STRONG_TREND_COEF,
|
||||
RANGE_COEF,
|
||||
HIGH_VOL_EXTRA_CONFIRM,
|
||||
)
|
||||
from adaptive_third_strategy.indicators import (
|
||||
get_ema_atr_from_klines,
|
||||
align_higher_tf_ema,
|
||||
ema,
|
||||
)
|
||||
|
||||
|
||||
def get_body_size(candle: Dict) -> float:
|
||||
return abs(float(candle["open"]) - float(candle["close"]))
|
||||
|
||||
|
||||
def is_bullish(candle: Dict) -> bool:
|
||||
return float(candle["close"]) > float(candle["open"])
|
||||
|
||||
|
||||
def get_min_body_threshold(price: float, atr_value: Optional[float]) -> float:
|
||||
"""有效K线最小实体 = max(ATR*0.1, 价格*0.05%)"""
|
||||
min_vol = price * MIN_VOLATILITY_PERCENT
|
||||
if atr_value is not None and atr_value > 0:
|
||||
min_vol = max(min_vol, atr_value * MIN_BODY_ATR_RATIO)
|
||||
return min_vol
|
||||
|
||||
|
||||
def find_valid_prev_bar(
|
||||
all_data: List[Dict],
|
||||
current_idx: int,
|
||||
atr_series: List[Optional[float]],
|
||||
min_body_override: Optional[float] = None,
|
||||
) -> Tuple[Optional[int], Optional[Dict]]:
|
||||
"""从当前索引往前找实体>=阈值的K线。阈值 = max(ATR*0.1, 价格*0.05%)"""
|
||||
if current_idx <= 0:
|
||||
return None, None
|
||||
for i in range(current_idx - 1, -1, -1):
|
||||
prev = all_data[i]
|
||||
body = get_body_size(prev)
|
||||
price = float(prev["close"])
|
||||
atr_val = atr_series[i] if i < len(atr_series) else None
|
||||
th = min_body_override if min_body_override is not None else get_min_body_threshold(price, atr_val)
|
||||
if body >= th:
|
||||
return i, prev
|
||||
return None, None
|
||||
|
||||
|
||||
def get_trend(
|
||||
klines_5m: List[Dict],
|
||||
idx_5m: int,
|
||||
ema_5m: List[Optional[float]],
|
||||
ema_15m_align: List[Dict],
|
||||
ema_60m_align: List[Dict],
|
||||
) -> str:
|
||||
"""
|
||||
多时间框架趋势。返回 "long" / "short" / "neutral"。
|
||||
长期:1h EMA50 vs EMA200;中期:15m EMA20 vs EMA50;短期:5m close vs EMA9。
|
||||
ema_*_align: 与 5m 对齐的列表,每项 {"ema_fast", "ema_slow"}。
|
||||
"""
|
||||
if idx_5m >= len(klines_5m):
|
||||
return "neutral"
|
||||
curr = klines_5m[idx_5m]
|
||||
close_5 = float(curr["close"])
|
||||
# 短期
|
||||
ema9 = ema_5m[idx_5m] if idx_5m < len(ema_5m) else None
|
||||
short_bull = (ema9 is not None and close_5 > ema9)
|
||||
# 中期
|
||||
mid = ema_15m_align[idx_5m] if idx_5m < len(ema_15m_align) else {}
|
||||
mid_bull = (mid.get("ema_fast") is not None and mid.get("ema_slow") is not None
|
||||
and mid["ema_fast"] > mid["ema_slow"])
|
||||
# 长期
|
||||
long_d = ema_60m_align[idx_5m] if idx_5m < len(ema_60m_align) else {}
|
||||
long_bull = (long_d.get("ema_fast") is not None and long_d.get("ema_slow") is not None
|
||||
and long_d["ema_fast"] > long_d["ema_slow"])
|
||||
|
||||
if TREND_MODE == "aggressive":
|
||||
return "long" if short_bull else "short"
|
||||
if TREND_MODE == "conservative":
|
||||
if mid_bull and short_bull:
|
||||
return "long"
|
||||
if not mid_bull and not short_bull:
|
||||
return "short"
|
||||
return "neutral"
|
||||
# strict
|
||||
if long_bull and mid_bull and short_bull:
|
||||
return "long"
|
||||
if not long_bull and not mid_bull and not short_bull:
|
||||
return "short"
|
||||
return "neutral"
|
||||
|
||||
|
||||
def get_dynamic_trigger_levels(
|
||||
prev: Dict,
|
||||
atr_value: float,
|
||||
trend: str,
|
||||
market_state: str = "normal",
|
||||
) -> Tuple[Optional[float], Optional[float]]:
|
||||
"""
|
||||
动态三分位触发价。
|
||||
波动率系数 = clamp(实体/ATR, 0.3, 3.0),调整系数 = 0.33 * 波动率系数。
|
||||
顺势方向 ×0.8,逆势 ×1.2。
|
||||
"""
|
||||
body = get_body_size(prev)
|
||||
if body < 1e-6 or atr_value <= 0:
|
||||
return None, None
|
||||
vol_coef = body / atr_value
|
||||
vol_coef = max(VOLATILITY_COEF_CLAMP[0], min(VOLATILITY_COEF_CLAMP[1], vol_coef))
|
||||
adj = BASE_COEF * vol_coef
|
||||
if market_state == "strong_trend":
|
||||
adj = STRONG_TREND_COEF
|
||||
elif market_state == "range":
|
||||
adj = RANGE_COEF
|
||||
p_close = float(prev["close"])
|
||||
if trend == "long":
|
||||
long_adj = adj * TREND_FAVOR_COEF
|
||||
short_adj = adj * TREND_AGAINST_COEF
|
||||
elif trend == "short":
|
||||
long_adj = adj * TREND_AGAINST_COEF
|
||||
short_adj = adj * TREND_FAVOR_COEF
|
||||
else:
|
||||
long_adj = short_adj = adj
|
||||
long_trigger = p_close + body * long_adj
|
||||
short_trigger = p_close - body * short_adj
|
||||
return long_trigger, short_trigger
|
||||
|
||||
|
||||
def check_signal_confirm(
|
||||
curr: Dict,
|
||||
direction: str,
|
||||
trigger_price: float,
|
||||
all_data: List[Dict],
|
||||
current_idx: int,
|
||||
volume_ma: Optional[float],
|
||||
required: int = CONFIRM_REQUIRED,
|
||||
) -> int:
|
||||
"""
|
||||
确认条件计数:收盘价确认、成交量确认、动量确认。
|
||||
返回满足的个数。
|
||||
"""
|
||||
count = 0
|
||||
c_close = float(curr["close"])
|
||||
c_volume = float(curr.get("volume", 0))
|
||||
# 1. 收盘价确认
|
||||
if direction == "long" and c_close >= trigger_price:
|
||||
count += 1
|
||||
elif direction == "short" and c_close <= trigger_price:
|
||||
count += 1
|
||||
# 2. 成交量确认
|
||||
if volume_ma is not None and volume_ma > 0 and c_volume >= volume_ma * VOLUME_RATIO_THRESHOLD:
|
||||
count += 1
|
||||
# 3. 动量确认:当前K线实体方向与信号一致
|
||||
if direction == "long" and is_bullish(curr):
|
||||
count += 1
|
||||
elif direction == "short" and not is_bullish(curr):
|
||||
count += 1
|
||||
return count
|
||||
|
||||
|
||||
def in_forbidden_period(ts_sec: int) -> bool:
|
||||
"""是否在禁止交易时段(按 UTC+8 小时:分)"""
|
||||
from datetime import datetime, timezone
|
||||
try:
|
||||
dt = datetime.fromtimestamp(ts_sec, tz=timezone.utc)
|
||||
except Exception:
|
||||
dt = datetime.utcfromtimestamp(ts_sec)
|
||||
# 转 UTC+8
|
||||
hour = (dt.hour + 8) % 24
|
||||
minute = dt.minute
|
||||
for h1, m1, h2, m2 in FORBIDDEN_PERIODS:
|
||||
t1 = h1 * 60 + m1
|
||||
t2 = h2 * 60 + m2
|
||||
t = hour * 60 + minute
|
||||
if t1 <= t < t2:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_market_state(
|
||||
atr_value: float,
|
||||
atr_avg: Optional[float],
|
||||
trend: str,
|
||||
) -> str:
|
||||
"""normal / strong_trend / range / high_vol"""
|
||||
if atr_avg is not None and atr_avg > 0 and atr_value >= atr_avg * ATR_PAUSE_MULT:
|
||||
return "high_vol"
|
||||
if trend in ("long", "short") and atr_avg is not None and atr_value > atr_avg * 1.2:
|
||||
return "strong_trend"
|
||||
if trend == "neutral":
|
||||
return "range"
|
||||
return "normal"
|
||||
|
||||
|
||||
def check_trigger(
|
||||
all_data: List[Dict],
|
||||
current_idx: int,
|
||||
atr_series: List[Optional[float]],
|
||||
ema_5m: List[Optional[float]],
|
||||
ema_15m_align: List[Dict],
|
||||
ema_60m_align: List[Dict],
|
||||
volume_ma_list: Optional[List[Optional[float]]] = None,
|
||||
use_confirm: bool = True,
|
||||
) -> Tuple[Optional[str], Optional[float], Optional[int], Optional[Dict]]:
|
||||
"""
|
||||
检查当前K线是否产生有效信号(含趋势过滤与确认)。
|
||||
返回 (方向, 触发价, 有效前一根索引, 有效前一根K线) 或 (None, None, None, None)。
|
||||
"""
|
||||
if current_idx <= 0 or current_idx >= len(all_data):
|
||||
return None, None, None, None
|
||||
curr = all_data[current_idx]
|
||||
valid_prev_idx, prev = find_valid_prev_bar(all_data, current_idx, atr_series)
|
||||
if prev is None:
|
||||
return None, None, None, None
|
||||
atr_val = atr_series[current_idx] if current_idx < len(atr_series) else None
|
||||
if atr_val is None or atr_val <= 0:
|
||||
return None, None, None, None
|
||||
trend = get_trend(
|
||||
all_data, current_idx, ema_5m, ema_15m_align, ema_60m_align,
|
||||
)
|
||||
atr_avg = None
|
||||
if atr_series:
|
||||
valid_atr = [x for x in atr_series[: current_idx + 1] if x is not None and x > 0]
|
||||
if len(valid_atr) >= ATR_PERIOD:
|
||||
atr_avg = sum(valid_atr) / len(valid_atr)
|
||||
market_state = get_market_state(atr_val, atr_avg, trend)
|
||||
if market_state == "high_vol":
|
||||
return None, None, None, None
|
||||
long_trigger, short_trigger = get_dynamic_trigger_levels(prev, atr_val, trend, market_state)
|
||||
if long_trigger is None:
|
||||
return None, None, None, None
|
||||
c_high = float(curr["high"])
|
||||
c_low = float(curr["low"])
|
||||
long_triggered = c_high >= long_trigger
|
||||
short_triggered = c_low <= short_trigger
|
||||
direction = None
|
||||
trigger_price = None
|
||||
if long_triggered and short_triggered:
|
||||
c_open = float(curr["open"])
|
||||
if abs(c_open - short_trigger) <= abs(c_open - long_trigger):
|
||||
direction, trigger_price = "short", short_trigger
|
||||
else:
|
||||
direction, trigger_price = "long", long_trigger
|
||||
elif short_triggered:
|
||||
direction, trigger_price = "short", short_trigger
|
||||
elif long_triggered:
|
||||
direction, trigger_price = "long", long_trigger
|
||||
if direction is None:
|
||||
return None, None, None, None
|
||||
# 趋势过滤:逆势不交易(可选,这里做过滤)
|
||||
if trend == "long" and direction == "short":
|
||||
return None, None, None, None
|
||||
if trend == "short" and direction == "long":
|
||||
return None, None, None, None
|
||||
# 禁止时段
|
||||
if in_forbidden_period(curr["id"]):
|
||||
return None, None, None, None
|
||||
# 信号确认
|
||||
if use_confirm and CONFIRM_REQUIRED > 0:
|
||||
vol_ma = volume_ma_list[current_idx] if volume_ma_list and current_idx < len(volume_ma_list) else None
|
||||
n = check_signal_confirm(curr, direction, trigger_price, all_data, current_idx, vol_ma, CONFIRM_REQUIRED)
|
||||
if n < CONFIRM_REQUIRED:
|
||||
return None, None, None, None
|
||||
return direction, trigger_price, valid_prev_idx, prev
|
||||
|
||||
|
||||
def build_volume_ma(klines: List[Dict], period: int = VOLUME_MA_PERIOD) -> List[Optional[float]]:
|
||||
"""前 period-1 为 None,之后为 volume 的 SMA"""
|
||||
vol = [float(k.get("volume", 0)) for k in klines]
|
||||
out: List[Optional[float]] = [None] * (period - 1)
|
||||
for i in range(period - 1, len(vol)):
|
||||
out.append(sum(vol[i - period + 1 : i + 1]) / period)
|
||||
return out
|
||||
@@ -1,187 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
自适应三分位趋势策略 - 实盘交易
|
||||
拉取 Bitmart 5/15/60 分钟数据,计算信号,执行开平仓(沿用 交易/bitmart-三分之一策略交易 的浏览器/API 逻辑)
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import datetime
|
||||
from typing import List, Dict, Optional, Tuple
|
||||
try:
|
||||
from loguru import logger
|
||||
except ImportError:
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
if ROOT_DIR not in sys.path:
|
||||
sys.path.insert(0, ROOT_DIR)
|
||||
|
||||
from bitmart.api_contract import APIContract
|
||||
from adaptive_third_strategy.config import (
|
||||
STEP_5M,
|
||||
STEP_15M,
|
||||
STEP_60M,
|
||||
ATR_PERIOD,
|
||||
EMA_SHORT,
|
||||
EMA_MID_FAST,
|
||||
EMA_MID_SLOW,
|
||||
EMA_LONG_FAST,
|
||||
EMA_LONG_SLOW,
|
||||
BASE_POSITION_PERCENT,
|
||||
MAX_POSITION_PERCENT,
|
||||
CONTRACT_SYMBOL,
|
||||
)
|
||||
from adaptive_third_strategy.indicators import get_ema_atr_from_klines, align_higher_tf_ema
|
||||
from adaptive_third_strategy.strategy_core import check_trigger, build_volume_ma
|
||||
from adaptive_third_strategy.data_fetcher import fetch_klines, _format_bar
|
||||
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
class AdaptiveThirdStrategyTrader:
|
||||
"""实盘:获取多周期K线 -> 策略信号 -> 开平仓(可接浏览器或纯 API)"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
api_key: str,
|
||||
secret_key: str,
|
||||
memo: str = "合约交易",
|
||||
symbol: str = CONTRACT_SYMBOL,
|
||||
bit_id: Optional[str] = None,
|
||||
):
|
||||
self.api_key = api_key
|
||||
self.secret_key = secret_key
|
||||
self.memo = memo
|
||||
self.symbol = symbol
|
||||
self.bit_id = bit_id
|
||||
self.contractAPI = APIContract(api_key, secret_key, memo, timeout=(5, 15))
|
||||
self.check_interval = 3
|
||||
self.last_trigger_kline_id: Optional[int] = None
|
||||
self.last_trigger_direction: Optional[str] = None
|
||||
self.last_trade_kline_id: Optional[int] = None
|
||||
self.position_direction: int = 0 # 1 多 -1 空 0 无
|
||||
|
||||
def get_klines_multi(self) -> Tuple[List[Dict], List[Dict], List[Dict]]:
|
||||
"""拉取当前 5/15/60 分钟 K 线(最近约 3 小时足够算 EMA/ATR)"""
|
||||
end_ts = int(time.time())
|
||||
start_ts = end_ts - 3600 * 3
|
||||
k5 = fetch_klines(self.contractAPI, STEP_5M, start_ts, end_ts, self.symbol)
|
||||
k15 = fetch_klines(self.contractAPI, STEP_15M, start_ts, end_ts, self.symbol)
|
||||
k60 = fetch_klines(self.contractAPI, STEP_60M, start_ts, end_ts, self.symbol)
|
||||
return k5, k15, k60
|
||||
|
||||
def get_position_status(self) -> bool:
|
||||
"""查询当前持仓,更新 self.position_direction"""
|
||||
try:
|
||||
response = self.contractAPI.get_position(contract_symbol=self.symbol)[0]
|
||||
if response.get("code") != 1000:
|
||||
return False
|
||||
positions = response.get("data", [])
|
||||
if not positions:
|
||||
self.position_direction = 0
|
||||
return True
|
||||
self.position_direction = 1 if positions[0].get("position_type") == 1 else -1
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"持仓查询异常: {e}")
|
||||
return False
|
||||
|
||||
def check_realtime_signal(
|
||||
self,
|
||||
klines_5m: List[Dict],
|
||||
klines_15m: List[Dict],
|
||||
klines_60m: List[Dict],
|
||||
) -> Tuple[Optional[str], Optional[float], Optional[Dict]]:
|
||||
"""
|
||||
基于当前 K 线(最后一根未收盘)检测实时信号。
|
||||
返回 (方向, 触发价, 当前K线) 或 (None, None, None)。
|
||||
"""
|
||||
if len(klines_5m) < ATR_PERIOD + 2:
|
||||
return None, None, None
|
||||
from adaptive_third_strategy.indicators import get_ema_atr_from_klines
|
||||
from adaptive_third_strategy.strategy_core import check_trigger
|
||||
ema_5m, atr_5m = get_ema_atr_from_klines(klines_5m, EMA_SHORT, ATR_PERIOD)
|
||||
ema_15m_align = align_higher_tf_ema(klines_5m, klines_15m, EMA_MID_FAST, EMA_MID_SLOW)
|
||||
ema_60m_align = align_higher_tf_ema(klines_5m, klines_60m, EMA_LONG_FAST, EMA_LONG_SLOW)
|
||||
volume_ma = build_volume_ma(klines_5m)
|
||||
curr_idx = len(klines_5m) - 1
|
||||
direction, trigger_price, _, _ = check_trigger(
|
||||
klines_5m, curr_idx, atr_5m, ema_5m, ema_15m_align, ema_60m_align, volume_ma, use_confirm=True
|
||||
)
|
||||
curr = klines_5m[curr_idx]
|
||||
curr_id = curr["id"]
|
||||
if direction is None:
|
||||
return None, None, None
|
||||
if self.last_trigger_kline_id == curr_id and self.last_trigger_direction == direction:
|
||||
return None, None, None
|
||||
if self.last_trade_kline_id == curr_id:
|
||||
return None, None, None
|
||||
if (direction == "long" and self.position_direction == 1) or (direction == "short" and self.position_direction == -1):
|
||||
return None, None, None
|
||||
return direction, trigger_price, curr
|
||||
|
||||
def run_loop(self, ding_callback=None):
|
||||
"""
|
||||
主循环:拉数据 -> 检测信号 -> 若需交易则调用开平仓(需外部实现或注入)。
|
||||
ding_callback(msg, error=False) 可选,用于钉钉通知。
|
||||
"""
|
||||
def ding(msg: str, error: bool = False):
|
||||
if ding_callback:
|
||||
ding_callback(msg, error)
|
||||
else:
|
||||
if error:
|
||||
logger.error(msg)
|
||||
else:
|
||||
logger.info(msg)
|
||||
|
||||
logger.info("自适应三分位趋势策略实盘启动,按 Bitmart 方式拉取 5/15/60 分钟数据")
|
||||
while True:
|
||||
try:
|
||||
k5, k15, k60 = self.get_klines_multi()
|
||||
if len(k5) < ATR_PERIOD + 2:
|
||||
time.sleep(self.check_interval)
|
||||
continue
|
||||
if not self.get_position_status():
|
||||
time.sleep(self.check_interval)
|
||||
continue
|
||||
direction, trigger_price, curr = self.check_realtime_signal(k5, k15, k60)
|
||||
if direction and trigger_price is not None and curr is not None:
|
||||
curr_id = curr["id"]
|
||||
ding(f"信号: {direction} @ {trigger_price:.2f} 当前K线 id={curr_id}")
|
||||
# 这里接入你的开平仓实现:平仓 + 开单(可调用 交易/bitmart-三分之一策略交易 的 平仓/开单 或 API 下单)
|
||||
# 示例:仅记录,不实际下单
|
||||
self.last_trigger_kline_id = curr_id
|
||||
self.last_trigger_direction = direction
|
||||
self.last_trade_kline_id = curr_id
|
||||
time.sleep(self.check_interval)
|
||||
except Exception as e:
|
||||
logger.exception(e)
|
||||
time.sleep(60)
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description="自适应三分位趋势策略实盘")
|
||||
parser.add_argument("--api-key", default="", help="Bitmart API Key")
|
||||
parser.add_argument("--secret-key", default="", help="Bitmart Secret Key")
|
||||
parser.add_argument("--bit-id", default="", help="比特浏览器 ID(若用浏览器下单)")
|
||||
args = parser.parse_args()
|
||||
api_key = args.api_key or os.environ.get("BITMART_API_KEY", "a0fb7b98464fd9bcce67e7c519d58ec10d0c38a8")
|
||||
secret_key = args.secret_key or os.environ.get("BITMART_SECRET_KEY", "4eaeba78e77aeaab1c2027f846a276d164f264a44c2c1bb1c5f3be50c8de1ca5")
|
||||
trader = AdaptiveThirdStrategyTrader(api_key, secret_key, bit_id=args.bit_id or None)
|
||||
try:
|
||||
from 交易.tools import send_dingtalk_message
|
||||
def ding(msg, error=False):
|
||||
prefix = "❌自适应三分位:" if error else "🔔自适应三分位:"
|
||||
send_dingtalk_message(f"{prefix}{msg}")
|
||||
except Exception:
|
||||
def ding(msg, error=False):
|
||||
logger.info(msg)
|
||||
trader.run_loop(ding_callback=ding)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
139
bieget/main.py
139
bieget/main.py
@@ -1,139 +0,0 @@
|
||||
import requests
|
||||
import datetime
|
||||
|
||||
cookies = {
|
||||
'bitget_lang': 'zh-CN',
|
||||
'_dx_kvani5r': '24e0dceb12f85d9a1279183ca29c09f772f8c61124e4eca228d8dd909c9b24b0019c2b9a',
|
||||
'OptanonAlertBoxClosed': 'Wed%20Sep%2024%202025%2017:12:32%20GMT+0800%20(%E4%B8%AD%E5%9B%BD%E6%A0%87%E5%87%86%E6%97%B6%E9%97%B4)',
|
||||
'OptanonConsent': 'isMarketing=1&isStatistic=1',
|
||||
'_cfuvid': 'IHXjOVVjx5iewfwL1nrgeHsZ6whu9s4iYYYvNvJvOQM-1758867462928-0.0.1.1-604800000',
|
||||
'dy_token': '68d630093VQFDNL1nUkv46lfDdhjHGYpoz9dddX1',
|
||||
'g_state': '{"i_l":0}',
|
||||
'theme': 'white',
|
||||
'BITGET_LOCAL_COOKIE': '{%22bitget_lang%22:%22zh-CN%22%2C%22bitget_unit%22:%22CNY%22%2C%22bitget_showasset%22:true%2C%22bitget_theme%22:%22white%22%2C%22bitget_layout%22:%22right%22%2C%22bitget_valuationunit%22:0%2C%22bitget_valuationunit_new%22:1%2C%22bitget_valuationunitandfiat%22:1%2C%22bitgt_login%22:false%2C%22theme%22:%22black%22%2C%22brand_simulation_trade%22:false%2C%22bitget_currency%22:%22%22}',
|
||||
'bt_rtoken': 'upex:session:id:1d00ce6487e37f4d7230a5aafe3ffc82e799156a86e9778bacb2310c7cf39b98',
|
||||
'bt_uid': '619BB9EC4D4A3F9FC1AC092D38818A00',
|
||||
'bt_sessonid': '0bf4ef42-798c-4f8d-8fe5-c07364d3b6b5',
|
||||
'bt_newsessionid': 'eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ZjJmNDBjYS0zNTkyLTRiNmItOTdiMy01OWQ5MTU5Y2E2YmQ0NzQ5ODIwMTAiLCJ1aWQiOiIxV00rVjRqcTJYSVRQemdJVWhUMnNRPT0iLCJzdWIiOiJsenoqKipvbSIsImlwIjoiM3dNK05yTkxjQXVQTG95dWZkWkI1QT09IiwiZGlkIjoiSjBZZjQ1NkV5RUxaSGhaa2tWYnNQZTVlSFVYYUNKZS9CM21wTHJ5SHJWMTEyUklzWlFLTTVIQmtTNHlUUmhuVyIsInN0cyI6MCwiaWF0IjoxNzU4ODY3NTgxLCJleHAiOjE3NTkyOTk1ODEsInB1c2hpZCI6ImRka1NMR1VDak9Sd1pFdU1rMFlaMWc9PSIsImlzcyI6InVwZXgifQ.Buwhs7UF7dOnOAso8l-RIyc_A47UdWSeVMiwIonOdiY',
|
||||
'USD': 'CNY',
|
||||
'__cf_bm': '3AMy5P3a.qp5uEqe9d_DonCDcL621CKULNKaUpfn2rE-1758868368-1.0.1.1-JzjFQidqwDxVV3gJH8ls0NeCGe.nsK.SFYP0hPXUcupuLTBpscmBYQRMfAjeOlnaZRBsQROFL2fETfXatDwQwDHU7aHOT4IMwECKB3sTIN4',
|
||||
}
|
||||
|
||||
headers = {
|
||||
'accept': 'application/json, text/plain, */*',
|
||||
'accept-language': 'zh,zh-CN;q=0.9,zh-HK;q=0.8,en;q=0.7',
|
||||
'baggage': 'sentry-environment=online,sentry-release=905e3a416e2b0ad3c98a82ab9b94dd87f1a5c1fd,sentry-public_key=8aeef93d782e4ef5ae75634bfee9b590,sentry-trace_id=e7afec8af26a4480893c8b58257af49b',
|
||||
'cache-control': 'no-cache',
|
||||
'custom-token': '765eb29bf89843258756f8851ed5a48f',
|
||||
'deviceid': '7c88d47fdc39e5c422eff197b10d3485',
|
||||
'dy-token': '68d630093VQFDNL1nUkv46lfDdhjHGYpoz9dddX1',
|
||||
'language': 'zh_CN',
|
||||
'locale': 'zh_CN',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=1, i',
|
||||
'referer': 'https://www.bitget.com/zh-CN/futures/usdt/ETHUSDT',
|
||||
'sec-ch-ua': '"Chromium";v="140", "Not=A?Brand";v="24", "Google Chrome";v="140"',
|
||||
'sec-ch-ua-mobile': '?0',
|
||||
'sec-ch-ua-platform': '"Windows"',
|
||||
'sec-fetch-dest': 'empty',
|
||||
'sec-fetch-mode': 'cors',
|
||||
'sec-fetch-site': 'same-origin',
|
||||
'sentry-trace': 'e7afec8af26a4480893c8b58257af49b-8aacec1747b38bd7-0',
|
||||
'terminalcode': 'cc98a9dd01af7e2d50d9b9858723b25e',
|
||||
'terminaltype': '1',
|
||||
'tm': '1758868503025',
|
||||
'uhti': 'w17588685039139806ab12716',
|
||||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36',
|
||||
'website': 'mix',
|
||||
# 'cookie': 'bitget_lang=zh-CN; _dx_kvani5r=24e0dceb12f85d9a1279183ca29c09f772f8c61124e4eca228d8dd909c9b24b0019c2b9a; OptanonAlertBoxClosed=Wed%20Sep%2024%202025%2017:12:32%20GMT+0800%20(%E4%B8%AD%E5%9B%BD%E6%A0%87%E5%87%86%E6%97%B6%E9%97%B4); OptanonConsent=isMarketing=1&isStatistic=1; _cfuvid=IHXjOVVjx5iewfwL1nrgeHsZ6whu9s4iYYYvNvJvOQM-1758867462928-0.0.1.1-604800000; dy_token=68d630093VQFDNL1nUkv46lfDdhjHGYpoz9dddX1; g_state={"i_l":0}; theme=white; BITGET_LOCAL_COOKIE={%22bitget_lang%22:%22zh-CN%22%2C%22bitget_unit%22:%22CNY%22%2C%22bitget_showasset%22:true%2C%22bitget_theme%22:%22white%22%2C%22bitget_layout%22:%22right%22%2C%22bitget_valuationunit%22:0%2C%22bitget_valuationunit_new%22:1%2C%22bitget_valuationunitandfiat%22:1%2C%22bitgt_login%22:false%2C%22theme%22:%22black%22%2C%22brand_simulation_trade%22:false%2C%22bitget_currency%22:%22%22}; bt_rtoken=upex:session:id:1d00ce6487e37f4d7230a5aafe3ffc82e799156a86e9778bacb2310c7cf39b98; bt_uid=619BB9EC4D4A3F9FC1AC092D38818A00; bt_sessonid=0bf4ef42-798c-4f8d-8fe5-c07364d3b6b5; bt_newsessionid=eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ZjJmNDBjYS0zNTkyLTRiNmItOTdiMy01OWQ5MTU5Y2E2YmQ0NzQ5ODIwMTAiLCJ1aWQiOiIxV00rVjRqcTJYSVRQemdJVWhUMnNRPT0iLCJzdWIiOiJsenoqKipvbSIsImlwIjoiM3dNK05yTkxjQXVQTG95dWZkWkI1QT09IiwiZGlkIjoiSjBZZjQ1NkV5RUxaSGhaa2tWYnNQZTVlSFVYYUNKZS9CM21wTHJ5SHJWMTEyUklzWlFLTTVIQmtTNHlUUmhuVyIsInN0cyI6MCwiaWF0IjoxNzU4ODY3NTgxLCJleHAiOjE3NTkyOTk1ODEsInB1c2hpZCI6ImRka1NMR1VDak9Sd1pFdU1rMFlaMWc9PSIsImlzcyI6InVwZXgifQ.Buwhs7UF7dOnOAso8l-RIyc_A47UdWSeVMiwIonOdiY; USD=CNY; __cf_bm=3AMy5P3a.qp5uEqe9d_DonCDcL621CKULNKaUpfn2rE-1758868368-1.0.1.1-JzjFQidqwDxVV3gJH8ls0NeCGe.nsK.SFYP0hPXUcupuLTBpscmBYQRMfAjeOlnaZRBsQROFL2fETfXatDwQwDHU7aHOT4IMwECKB3sTIN4',
|
||||
}
|
||||
if __name__ == '__main__':
|
||||
|
||||
params = {
|
||||
'symbolId': 'ETHUSDT_UMCBL',
|
||||
'kLineStep': '1m',
|
||||
'kLineType': '1',
|
||||
'endTime': '1758864600000',
|
||||
}
|
||||
|
||||
response = requests.get(
|
||||
'https://www.bitget.com/v1/kline/getMoreKlineDataV2',
|
||||
params=params, cookies=cookies, headers=headers
|
||||
)
|
||||
|
||||
raw_data = response.json()["data"]
|
||||
|
||||
print(raw_data)
|
||||
|
||||
# 转换成字典格式,方便后续处理
|
||||
data = []
|
||||
for i in raw_data:
|
||||
data.append({
|
||||
'id': int(i[0]) // 1000, # 秒级时间戳
|
||||
'open': float(i[1]),
|
||||
'high': float(i[2]),
|
||||
'low': float(i[3]),
|
||||
'close': float(i[4]),
|
||||
})
|
||||
|
||||
print(i)
|
||||
|
||||
print(data)
|
||||
|
||||
# 按时间排序
|
||||
sorted_data = sorted(data, key=lambda x: x['id'])
|
||||
|
||||
print(sorted_data)
|
||||
|
||||
signals = 0
|
||||
wins = 0
|
||||
lig_price = 0
|
||||
low_price = 0
|
||||
|
||||
for idx in range(1, len(sorted_data) - 2): # 至少留两根 K 线验证
|
||||
prev = sorted_data[idx - 1]
|
||||
curr = sorted_data[idx]
|
||||
future = sorted_data[idx + 2]
|
||||
|
||||
prev_open, prev_close = prev['open'], prev['close']
|
||||
curr_open, curr_close = curr['open'], curr['close']
|
||||
future_close = future['close']
|
||||
|
||||
# 当前为涨
|
||||
if curr_close > curr_open:
|
||||
# 前一笔涨 + 包裹
|
||||
if prev_close > prev_open and curr_open < prev_open and curr_close > prev_close:
|
||||
signals += 1
|
||||
lig_price += (future_close - curr_close)
|
||||
if future_close > curr_close:
|
||||
wins += 1
|
||||
# 前一笔跌 + 反包
|
||||
elif prev_close < prev_open and curr_open < prev_close and curr_close > prev_open:
|
||||
signals += 1
|
||||
lig_price += (future_close - curr_close)
|
||||
if future_close > curr_close:
|
||||
wins += 1
|
||||
|
||||
# 当前为跌
|
||||
elif curr_close < curr_open:
|
||||
# 前一笔跌 + 包裹
|
||||
if prev_close < prev_open and curr_open > prev_open and curr_close < prev_close:
|
||||
signals += 1
|
||||
low_price += (curr_close - future_close)
|
||||
if future_close < curr_close:
|
||||
wins += 1
|
||||
# 前一笔涨 + 反包
|
||||
elif prev_close > prev_open and curr_open > prev_close and curr_close < prev_open:
|
||||
signals += 1
|
||||
low_price += (curr_close - future_close)
|
||||
if future_close < curr_close:
|
||||
wins += 1
|
||||
|
||||
if signals > 0:
|
||||
win_rate = wins / signals * 100
|
||||
print(f"信号数={signals}, 胜率={win_rate:.2f}%")
|
||||
else:
|
||||
print("没有找到符合条件的形态")
|
||||
|
||||
print("上涨盈亏合计:", lig_price)
|
||||
print("下跌盈亏合计:", low_price)
|
||||
@@ -1,81 +0,0 @@
|
||||
import requests
|
||||
|
||||
cookies = {
|
||||
'bitget_lang': 'zh-CN',
|
||||
'_dx_kvani5r': '24e0dceb12f85d9a1279183ca29c09f772f8c61124e4eca228d8dd909c9b24b0019c2b9a',
|
||||
'OptanonAlertBoxClosed': 'Wed%20Sep%2024%202025%2017:12:32%20GMT+0800%20(%E4%B8%AD%E5%9B%BD%E6%A0%87%E5%87%86%E6%97%B6%E9%97%B4)',
|
||||
'OptanonConsent': 'isMarketing=1&isStatistic=1',
|
||||
'g_state': '{"i_l":0}',
|
||||
'theme': 'white',
|
||||
'BITGET_LOCAL_COOKIE': '{%22bitget_lang%22:%22zh-CN%22%2C%22bitget_unit%22:%22CNY%22%2C%22bitget_showasset%22:true%2C%22bitget_theme%22:%22white%22%2C%22bitget_layout%22:%22right%22%2C%22bitget_valuationunit%22:0%2C%22bitget_valuationunit_new%22:1%2C%22bitget_valuationunitandfiat%22:1%2C%22bitgt_login%22:false%2C%22theme%22:%22black%22%2C%22brand_simulation_trade%22:false%2C%22bitget_currency%22:%22%22}',
|
||||
'USD': 'CNY',
|
||||
'_gcl_au': '1.1.737358653.1759055313',
|
||||
'_ga': 'GA1.1.942322417.1759055313',
|
||||
'afUserId': '58c62981-8c6f-4601-adee-77489e65d72b-p',
|
||||
'AF_SYNC': '1759974778836',
|
||||
'bt_rtoken': 'upex:session:id:d250cc2b001c88f9e9c60a3686fd7dcf5773a666402d41b35c7a6fa3f853392d',
|
||||
'bt_uid': '619BB9EC4D4A3F9FC1AC092D38818A00',
|
||||
'bt_sessonid': '6ad2dd3f-35f3-4ef8-accb-3292ceb8b5d7',
|
||||
'bt_newsessionid': 'eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI1NTk2ZjA1MC0zOGFhLTQxMjktOTcyZS0wMWUyZDI1MWI0N2M1NzQxNTgyOTIiLCJ1aWQiOiIxV00rVjRqcTJYSVRQemdJVWhUMnNRPT0iLCJzdWIiOiJsenoqKipvbSIsImlwIjoiWGRhSGJmdHZQVGVpeFlnMmxlVmdUZz09IiwiZGlkIjoiSjBZZjQ1NkV5RUxaSGhaa2tWYnNQZTVlSFVYYUNKZS9CM21wTHJ5SHJWMTEyUklzWlFLTTVIQmtTNHlUUmhuVyIsInN0cyI6MCwiaWF0IjoxNzU5OTc0NzkwLCJleHAiOjE3NjA0MDY3OTAsInB1c2hpZCI6ImRka1NMR1VDak9Sd1pFdU1rMFlaMWc9PSIsImlzcyI6InVwZXgifQ.0SygmCSR3f5tfA0xQpdzS241nFDbsVultPm0LmleYgY',
|
||||
'_ga_Z8Q93KHR0F': 'GS2.1.s1759974680$o4$g1$t1759974795$j19$l0$h0',
|
||||
'dy_token': '68e74d44zBITZniRPCwXG8rs7bnFrrlGIHyxtKx1',
|
||||
'_cfuvid': '7C9e7Tl9zudw90jhhHGEM4H7SJnpWoi39xepaHJpJuM-1759995357475-0.0.1.1-604800000',
|
||||
'__cf_bm': 'y7VR5vFWhYBo1IWnsrX2txdXsSeztAnTxMOQjyhyuf0-1760001832-1.0.1.1-kj32unDiaucuNb8pjHeq89WUlOf.zwJW6gyN53JjOsQ8FhB1R23kpDJtzc4878CdMKYfk7xFyYUnUBEiESvziCBFUbhm3F.R0qaPyZ.dw8g',
|
||||
}
|
||||
|
||||
headers = {
|
||||
'accept': 'application/json, text/plain, */*',
|
||||
'accept-language': 'zh-CN,zh;q=0.9,zh-HK;q=0.8,en;q=0.7',
|
||||
'baggage': 'sentry-environment=online,sentry-release=776c8c2125078b22570121601aabb051a52c0936,sentry-public_key=8aeef93d782e4ef5ae75634bfee9b590,sentry-trace_id=cb93656935cd47f289b30e55dd96f6c0',
|
||||
'cache-control': 'no-cache',
|
||||
'content-type': 'application/json;charset=UTF-8',
|
||||
'custom-token': '2facaf72193a42ab84dd879b94b57c7e',
|
||||
'deviceid': '7c88d47fdc39e5c422eff197b10d3485',
|
||||
'dy-token': '68e74d44zBITZniRPCwXG8rs7bnFrrlGIHyxtKx1',
|
||||
'language': 'zh_CN',
|
||||
'locale': 'zh_CN',
|
||||
'origin': 'https://www.bitget.com',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=1, i',
|
||||
'referer': 'https://www.bitget.com/zh-CN/futures/usdt/ETHUSDT',
|
||||
'sec-ch-ua': '"Google Chrome";v="141", "Not?A_Brand";v="8", "Chromium";v="141"',
|
||||
'sec-ch-ua-mobile': '?0',
|
||||
'sec-ch-ua-platform': '"Windows"',
|
||||
'sec-fetch-dest': 'empty',
|
||||
'sec-fetch-mode': 'cors',
|
||||
'sec-fetch-site': 'same-origin',
|
||||
'sentry-trace': 'cb93656935cd47f289b30e55dd96f6c0-b7272e524943c558-0',
|
||||
'terminalcode': 'cc98a9dd01af7e2d50d9b9858723b25e',
|
||||
'terminaltype': '1',
|
||||
'tm': '1760002404225',
|
||||
'uhti': 'w1760002409197066bf83bff8',
|
||||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36',
|
||||
'website': 'mix',
|
||||
# 'cookie': 'bitget_lang=zh-CN; _dx_kvani5r=24e0dceb12f85d9a1279183ca29c09f772f8c61124e4eca228d8dd909c9b24b0019c2b9a; OptanonAlertBoxClosed=Wed%20Sep%2024%202025%2017:12:32%20GMT+0800%20(%E4%B8%AD%E5%9B%BD%E6%A0%87%E5%87%86%E6%97%B6%E9%97%B4); OptanonConsent=isMarketing=1&isStatistic=1; g_state={"i_l":0}; theme=white; BITGET_LOCAL_COOKIE={%22bitget_lang%22:%22zh-CN%22%2C%22bitget_unit%22:%22CNY%22%2C%22bitget_showasset%22:true%2C%22bitget_theme%22:%22white%22%2C%22bitget_layout%22:%22right%22%2C%22bitget_valuationunit%22:0%2C%22bitget_valuationunit_new%22:1%2C%22bitget_valuationunitandfiat%22:1%2C%22bitgt_login%22:false%2C%22theme%22:%22black%22%2C%22brand_simulation_trade%22:false%2C%22bitget_currency%22:%22%22}; USD=CNY; _gcl_au=1.1.737358653.1759055313; _ga=GA1.1.942322417.1759055313; afUserId=58c62981-8c6f-4601-adee-77489e65d72b-p; AF_SYNC=1759974778836; bt_rtoken=upex:session:id:d250cc2b001c88f9e9c60a3686fd7dcf5773a666402d41b35c7a6fa3f853392d; bt_uid=619BB9EC4D4A3F9FC1AC092D38818A00; bt_sessonid=6ad2dd3f-35f3-4ef8-accb-3292ceb8b5d7; bt_newsessionid=eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI1NTk2ZjA1MC0zOGFhLTQxMjktOTcyZS0wMWUyZDI1MWI0N2M1NzQxNTgyOTIiLCJ1aWQiOiIxV00rVjRqcTJYSVRQemdJVWhUMnNRPT0iLCJzdWIiOiJsenoqKipvbSIsImlwIjoiWGRhSGJmdHZQVGVpeFlnMmxlVmdUZz09IiwiZGlkIjoiSjBZZjQ1NkV5RUxaSGhaa2tWYnNQZTVlSFVYYUNKZS9CM21wTHJ5SHJWMTEyUklzWlFLTTVIQmtTNHlUUmhuVyIsInN0cyI6MCwiaWF0IjoxNzU5OTc0NzkwLCJleHAiOjE3NjA0MDY3OTAsInB1c2hpZCI6ImRka1NMR1VDak9Sd1pFdU1rMFlaMWc9PSIsImlzcyI6InVwZXgifQ.0SygmCSR3f5tfA0xQpdzS241nFDbsVultPm0LmleYgY; _ga_Z8Q93KHR0F=GS2.1.s1759974680$o4$g1$t1759974795$j19$l0$h0; dy_token=68e74d44zBITZniRPCwXG8rs7bnFrrlGIHyxtKx1; _cfuvid=7C9e7Tl9zudw90jhhHGEM4H7SJnpWoi39xepaHJpJuM-1759995357475-0.0.1.1-604800000; __cf_bm=y7VR5vFWhYBo1IWnsrX2txdXsSeztAnTxMOQjyhyuf0-1760001832-1.0.1.1-kj32unDiaucuNb8pjHeq89WUlOf.zwJW6gyN53JjOsQ8FhB1R23kpDJtzc4878CdMKYfk7xFyYUnUBEiESvziCBFUbhm3F.R0qaPyZ.dw8g',
|
||||
}
|
||||
|
||||
json_data = {
|
||||
'startTime': 1759912500000,
|
||||
'endTime': 1760002409196,
|
||||
'symbolCode': 'ETHUSDT',
|
||||
'languageType': 1,
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
'https://www.bitget.com/v1/trigger/strategy/tradingview/queryHistorySignals',
|
||||
cookies=cookies,
|
||||
headers=headers,
|
||||
json=json_data,
|
||||
)
|
||||
|
||||
print(response.json())
|
||||
|
||||
# Note: json_data will not be serialized by requests
|
||||
# exactly as it was in the original request.
|
||||
# data = '{"startTime":1759912500000,"endTime":1760002409196,"symbolCode":"ETHUSDT","languageType":1}'
|
||||
# response = requests.post(
|
||||
# 'https://www.bitget.com/v1/trigger/strategy/tradingview/queryHistorySignals',
|
||||
# cookies=cookies,
|
||||
# headers=headers,
|
||||
# data=data,
|
||||
# )
|
||||
File diff suppressed because it is too large
Load Diff
418
bitmart/main.py
418
bitmart/main.py
@@ -1,418 +0,0 @@
|
||||
import re
|
||||
import json
|
||||
import hmac
|
||||
import time
|
||||
import base64
|
||||
import hashlib
|
||||
import datetime
|
||||
import requests
|
||||
|
||||
from tqdm import *
|
||||
from loguru import *
|
||||
from DrissionPage import *
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
|
||||
def is_bullish(c): # 阳线
|
||||
return float(c['close']) > float(c['open'])
|
||||
|
||||
|
||||
def is_bearish(c): # 阴线
|
||||
return float(c['close']) < float(c['open'])
|
||||
|
||||
|
||||
class WeexTransaction:
|
||||
def __init__(self, tge_id):
|
||||
self.tge_port = None # tge浏览器使用端口
|
||||
self.tge_id = tge_id # tge id
|
||||
self.tge_url = "http://127.0.0.1:50326" # tge本地服务url
|
||||
self.tge_headers = {
|
||||
"Authorization": f"Bearer asp_174003986c9b0799677c5b2c1adb76e402735d753bc91a91",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
# 替换为你自己的钉钉机器人 Webhook 地址
|
||||
self.webhook_url = "https://oapi.dingtalk.com/robot/send?access_token=e2fafb3f46866d50fe52cbb29650ba9ef1cbc97915dde238192f04c906fe4125"
|
||||
# 替换为你自己的钉钉机器人秘钥
|
||||
self.secret = "SEC5f320e72d7a4eaca540c66c3d09edff2f74936517390dee99ece6dd1b3611998"
|
||||
|
||||
self.page = None # 浏览器对象
|
||||
|
||||
self.start = 0 # 持仓状态 -1:做空,0:维持仓,1:做多
|
||||
self.kline_1 = None # 0:跌,1:涨
|
||||
self.kline_2 = None # 0:跌,1:涨
|
||||
|
||||
self.direction = None # 信号类型
|
||||
|
||||
self.pbar = None # 进度条对象
|
||||
|
||||
def get_signature(self, timestamp):
|
||||
# 将时间戳和密钥拼接
|
||||
string_to_sign = f'{timestamp}\n{self.secret}'
|
||||
string_to_sign = string_to_sign.encode('utf-8')
|
||||
# 使用 HMAC-SHA256 算法进行签名
|
||||
hmac_code = hmac.new(self.secret.encode('utf-8'), string_to_sign, digestmod=hashlib.sha256).digest()
|
||||
# 对签名结果进行 Base64 编码
|
||||
sign = base64.b64encode(hmac_code).decode('utf-8')
|
||||
return sign
|
||||
|
||||
# def send_dingtalk_message(self, message_content):
|
||||
# # 获取当前时间戳(毫秒)
|
||||
# timestamp = str(round(time.time() * 1000))
|
||||
# # 生成签名
|
||||
# sign = self.get_signature(timestamp, )
|
||||
# # 拼接带有签名信息的完整 Webhook URL
|
||||
# full_url = f"{self.webhook_url}×tamp={timestamp}&sign={sign}"
|
||||
#
|
||||
# # 定义消息内容
|
||||
# message = {
|
||||
# "msgtype": "text",
|
||||
# "text": {
|
||||
# "content": message_content
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# # 设置请求头
|
||||
# headers = {
|
||||
# "Content-Type": "application/json"
|
||||
# }
|
||||
#
|
||||
# try:
|
||||
# # 发送 POST 请求
|
||||
# response = requests.post(full_url, headers=headers, data=json.dumps(message))
|
||||
#
|
||||
# except requests.RequestException as e:
|
||||
# print(f"请求发生错误: {e}")
|
||||
|
||||
def send_dingtalk_message(self, message_content):
|
||||
|
||||
pass
|
||||
|
||||
# url = "http://8.137.99.82:9005/api/send_click?token=fegergauiernguie&phone=8613661496481"
|
||||
#
|
||||
# res = requests.post(
|
||||
# url=url,
|
||||
# json={
|
||||
# "phone": "8613661496481",
|
||||
# "bot_name": "ergggreef",
|
||||
# "datas": [
|
||||
# {"send_message": [message_content], "click_button": [""], },
|
||||
# ]
|
||||
#
|
||||
# }
|
||||
# )
|
||||
#
|
||||
# print(res.json())
|
||||
|
||||
def openBrowser(self, ): # 直接指定ID打开窗口,也可以使用 createBrowser 方法返回的ID
|
||||
|
||||
try:
|
||||
|
||||
response = requests.post(
|
||||
f"{self.tge_url}/api/browser/start",
|
||||
json={"envId": self.tge_id},
|
||||
headers=self.tge_headers
|
||||
)
|
||||
|
||||
self.tge_port = response.json()["data"]["port"]
|
||||
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def take_over_browser(self):
|
||||
try:
|
||||
co = ChromiumOptions()
|
||||
co.set_local_port(self.tge_port)
|
||||
|
||||
self.page = ChromiumPage(addr_or_opts=co)
|
||||
|
||||
self.page.set.window.max()
|
||||
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def is_bullish(self, c): # 阳线
|
||||
return float(c['close']) > float(c['open'])
|
||||
|
||||
def is_bearish(self, c): # 阴线
|
||||
return float(c['close']) < float(c['open'])
|
||||
|
||||
def check_signal(self, prev, curr):
|
||||
"""
|
||||
包住形态信号判定(仅15分钟K线):
|
||||
- 前跌后涨包住 -> 做多
|
||||
- 前涨后跌包住 -> 做空
|
||||
"""
|
||||
p_open, p_close = float(prev['open']), float(prev['close'])
|
||||
c_open, c_close = float(curr['open']), float(curr['close'])
|
||||
|
||||
# 前跌后涨包住 -> 做多
|
||||
if is_bullish(curr) and is_bearish(prev) and int(c_open) <= int(p_close) and int(c_close) >= int(p_open):
|
||||
return "long", "bear_bull_engulf"
|
||||
|
||||
# 前涨后跌包住 -> 做空
|
||||
if is_bearish(curr) and is_bullish(prev) and int(c_open) >= int(p_close) and int(c_close) <= int(p_open):
|
||||
return "short", "bull_bear_engulf"
|
||||
|
||||
return None, None
|
||||
|
||||
def get_price(self):
|
||||
|
||||
for i in range(3):
|
||||
try:
|
||||
logger.info(f"获取最新数据:{i + 1}次。。。")
|
||||
self.mn_tab.get(url="https://derivatives.bitmart.com/zh-CN/futures/ETHUSDT")
|
||||
res = self.mn_tab.listen.wait(timeout=25) # 等待并获取一个数据包
|
||||
|
||||
datas = []
|
||||
if res:
|
||||
|
||||
for data in res.response.body["data"]:
|
||||
insert_data = {
|
||||
'id': int(data["timestamp"]) - 1,
|
||||
'open': float(data["open"]),
|
||||
'high': float(data["high"]),
|
||||
'low': float(data["low"]),
|
||||
'close': float(data["close"])
|
||||
}
|
||||
|
||||
datas.append(insert_data)
|
||||
|
||||
return datas
|
||||
except:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
def remove_tags_and_spaces(self, html):
|
||||
# 创建 BeautifulSoup 对象
|
||||
soup = BeautifulSoup(html, 'html.parser')
|
||||
# 获取去除标签后的文本
|
||||
text = soup.get_text()
|
||||
# 去除所有空格
|
||||
text = text.replace(" ", "")
|
||||
return text
|
||||
|
||||
def to_do_page(self):
|
||||
# self.page.get("https://www.weeaxs.site/zh-CN/futures/demo-trading/ETH-SUSDT")
|
||||
|
||||
self.mn_tab.ele('x:(//button[normalize-space(text()) = "市价"])').click()
|
||||
time.sleep(1)
|
||||
|
||||
self.mn_tab.ele('x://*[@id="size_0"]').input(float(self.get_num()) / 100)
|
||||
time.sleep(1)
|
||||
|
||||
if self.direction == "long" and not self.start:
|
||||
logger.success(f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},开多")
|
||||
self.send_dingtalk_message(
|
||||
message_content=f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},开多")
|
||||
self.mn_tab.ele('x://span[normalize-space(text()) = "买入/做多"]').click()
|
||||
self.start = 1
|
||||
elif self.direction == "short" and not self.start:
|
||||
logger.success(f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},开空")
|
||||
self.send_dingtalk_message(
|
||||
message_content=f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},开空")
|
||||
self.mn_tab.ele('x://span[normalize-space(text()) = "卖出/做空"]').click()
|
||||
self.start = -1
|
||||
elif self.direction == "long" and self.start == -1:
|
||||
logger.success(f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},反手平空做多")
|
||||
self.send_dingtalk_message(
|
||||
message_content=f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},反手平空做多")
|
||||
self.mn_tab.ele('x:(//span[normalize-space(text()) = "市价"])').scroll.to_see(center=True)
|
||||
self.mn_tab.ele('x:(//span[normalize-space(text()) = "市价"])').click()
|
||||
time.sleep(3)
|
||||
self.mn_tab.ele('x:(//span[normalize-space(text()) = "买入/做多"])').click()
|
||||
self.start = 1
|
||||
elif self.direction == "short" and self.start == 1:
|
||||
logger.success(f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},反手平多做空")
|
||||
self.send_dingtalk_message(
|
||||
message_content=f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},反手平多做空")
|
||||
self.mn_tab.ele('x:(//span[normalize-space(text()) = "市价"])').scroll.to_see(center=True)
|
||||
self.mn_tab.ele('x:(//span[normalize-space(text()) = "市价"])').click()
|
||||
time.sleep(3)
|
||||
self.mn_tab.ele('x://span[normalize-space(text()) = "卖出/做空"]').click()
|
||||
self.start = -1
|
||||
|
||||
def get_text(self, target_text):
|
||||
# 去除目标文本中的空白字符
|
||||
cleaned_target_text = re.sub(r'\s', '', target_text)
|
||||
|
||||
# 创建 BeautifulSoup 对象
|
||||
soup = BeautifulSoup(self.mn_tab.html, 'html.parser')
|
||||
|
||||
# 遍历所有标签的文本内容
|
||||
for tag in soup.find_all():
|
||||
tag_text = tag.get_text()
|
||||
# 去除标签文本中的空白字符
|
||||
cleaned_tag_text = re.sub(r'\s', '', tag_text)
|
||||
if cleaned_target_text in cleaned_tag_text:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_now_time(self):
|
||||
# 获取当前时间戳
|
||||
current_timestamp = time.time()
|
||||
# 将当前时间戳转换为 datetime 对象
|
||||
current_datetime = datetime.datetime.fromtimestamp(current_timestamp)
|
||||
|
||||
# 计算距离当前时间最近的整点或 30 分时刻
|
||||
if current_datetime.minute < 30:
|
||||
target_datetime = current_datetime.replace(minute=0, second=0, microsecond=0)
|
||||
else:
|
||||
target_datetime = current_datetime.replace(minute=30, second=0, microsecond=0)
|
||||
|
||||
# 将目标 datetime 对象转换为时间戳
|
||||
target_timestamp = target_datetime.timestamp()
|
||||
|
||||
return int(target_timestamp)
|
||||
|
||||
def close_extra_tabs_in_browser(self):
|
||||
|
||||
try:
|
||||
for _, i in enumerate(self.page.get_tabs()):
|
||||
if _ == 0:
|
||||
continue
|
||||
|
||||
i.close()
|
||||
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
def get_num(self):
|
||||
|
||||
num_tab = self.page.new_tab()
|
||||
num_tab.listen.start("derivatives.bitmart.com/gw-api/contract-tiger/forward/v1/ifcontract/accounts")
|
||||
|
||||
for i in range(3):
|
||||
try:
|
||||
logger.info(f"获取最新数据:{i + 1}次。。。")
|
||||
num_tab.get(url="https://derivatives.bitmart.com/zh-CN/futures/ETHUSDT")
|
||||
res = num_tab.listen.wait(timeout=15) # 等待并获取一个数据包
|
||||
|
||||
if res:
|
||||
num_tab.close()
|
||||
return res.response.body["data"]["accounts"][0]["available_balance"]
|
||||
except:
|
||||
pass
|
||||
|
||||
num_tab.close()
|
||||
|
||||
return False
|
||||
|
||||
def action(self):
|
||||
# 获取比特端口
|
||||
if self.openBrowser():
|
||||
logger.info("获取打开比特成功,成功获取端口!!!")
|
||||
else:
|
||||
logger.error("打开比特失败!!!")
|
||||
return
|
||||
|
||||
# 接管浏览器
|
||||
if self.take_over_browser():
|
||||
logger.info("接管比特浏览器成功!!!")
|
||||
else:
|
||||
logger.error("接管浏览器失败!!!")
|
||||
return
|
||||
|
||||
if self.close_extra_tabs_in_browser():
|
||||
logger.info('关闭多余标签页成功!!!')
|
||||
else:
|
||||
logger.info('关闭多余标签页失败!!!')
|
||||
|
||||
self.mn_tab = self.page.new_tab()
|
||||
self.mn_tab.listen.start("contract-v2.bitmart.com/v1/ifcontract/quote/kline")
|
||||
logger.success("浏览器开启抓包模式。。。")
|
||||
|
||||
self.mn_tab.get(url="https://derivatives.bitmart.com/zh-CN/futures/ETHUSDT") # 打开网页
|
||||
|
||||
self.pbar = tqdm(total=30, desc="等待时间中", ncols=80) # desc:进度条说明,ncols:长度
|
||||
|
||||
while True:
|
||||
# 获取当前时间
|
||||
current_time = time.localtime()
|
||||
current_minute = current_time.tm_min
|
||||
|
||||
if current_minute < 30:
|
||||
self.pbar.n = current_minute
|
||||
self.pbar.refresh()
|
||||
else:
|
||||
self.pbar.n = current_minute - 30
|
||||
self.pbar.refresh()
|
||||
|
||||
if current_minute not in [0, 1, 2, 3, 4, 5, 30, 31, 32, 33, 34, ]: # 判断是否是 新的30分钟了
|
||||
# if current_minute not in range(60): # 判断是否是 新的30分钟了
|
||||
time.sleep(10)
|
||||
continue
|
||||
|
||||
new_price_datas = self.get_price()
|
||||
if new_price_datas:
|
||||
logger.success("获取最新交易价格成功!!!")
|
||||
else:
|
||||
logger.info("获取最新价格有问题!!!")
|
||||
continue
|
||||
|
||||
new_price_datas1 = sorted(new_price_datas, key=lambda x: x["id"])
|
||||
self.kline_1, self.kline_2, self.kline_3 = new_price_datas1[-3:]
|
||||
|
||||
# 判断抓取的数据是否正确
|
||||
if self.get_now_time() != self.kline_3["id"]:
|
||||
continue
|
||||
|
||||
time.sleep(15)
|
||||
|
||||
if self.get_text(target_text="全仓做多100X永续持仓"):
|
||||
self.start = 1
|
||||
elif self.get_text(target_text="全仓做空100X永续持仓"):
|
||||
self.start = -1
|
||||
else:
|
||||
self.start = 0
|
||||
|
||||
if self.start == 1:
|
||||
if is_bearish(self.kline_1) and is_bearish(self.kline_2):
|
||||
logger.success(f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},平多")
|
||||
self.send_dingtalk_message(
|
||||
message_content=f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},平多")
|
||||
|
||||
self.mn_tab.ele('x:(//span[normalize-space(text()) = "市价"])').scroll.to_see(center=True)
|
||||
self.mn_tab.ele('x:(//span[normalize-space(text()) = "市价"])').click()
|
||||
self.start = 0
|
||||
elif self.start == -1:
|
||||
if is_bullish(self.kline_1) and is_bullish(self.kline_2):
|
||||
logger.success(f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},平空")
|
||||
self.send_dingtalk_message(
|
||||
message_content=f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},平空")
|
||||
|
||||
self.mn_tab.ele('x:(//span[normalize-space(text()) = "市价"])').scroll.to_see(center=True)
|
||||
self.mn_tab.ele('x:(//span[normalize-space(text()) = "市价"])').click()
|
||||
self.start = 0
|
||||
|
||||
self.direction, signal_key = self.check_signal(prev=self.kline_1, curr=self.kline_2) # 判断信号
|
||||
|
||||
if self.direction:
|
||||
try:
|
||||
self.to_do_page()
|
||||
except Exception as e:
|
||||
self.send_dingtalk_message(
|
||||
message_content=f"{datetime.datetime.now()},{e}")
|
||||
|
||||
self.pbar.reset() # 重置进度条
|
||||
self.send_dingtalk_message(
|
||||
message_content=
|
||||
f"{datetime.datetime.now()},"
|
||||
f"目前有持仓:{"无" if self.start == 0 else ("多" if self.start == 1 else "空")},"
|
||||
f"当前信号:{"无" if not self.direction else ("多" if self.direction == "long" else "空")}"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
WeexTransaction(
|
||||
tge_id=196495,
|
||||
).action()
|
||||
|
||||
# //*[contains(text(), '特定文本')]
|
||||
@@ -1,29 +0,0 @@
|
||||
"""测试 BitMart API K线获取"""
|
||||
import time
|
||||
from bitmart.api_contract import APIContract
|
||||
|
||||
api_key = "a0fb7b98464fd9bcce67e7c519d58ec10d0c38a8"
|
||||
secret_key = "4eaeba78e77aeaab1c2027f846a276d164f264a44c2c1bb1c5f3be50c8de1ca5"
|
||||
memo = "数据抓取"
|
||||
|
||||
contractAPI = APIContract(api_key, secret_key, memo, timeout=(5, 15))
|
||||
|
||||
# 测试获取最近1小时的15分钟K线
|
||||
end_time = int(time.time())
|
||||
start_time = end_time - 3600 # 1小时前
|
||||
|
||||
print(f"当前时间戳: {end_time}")
|
||||
print(f"开始时间戳: {start_time}")
|
||||
print(f"当前时间: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(end_time))}")
|
||||
print(f"开始时间: {time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(start_time))}")
|
||||
|
||||
try:
|
||||
response = contractAPI.get_kline(
|
||||
contract_symbol="ETHUSDT",
|
||||
step=15,
|
||||
start_time=start_time,
|
||||
end_time=end_time
|
||||
)
|
||||
print(f"\n响应: {response}")
|
||||
except Exception as e:
|
||||
print(f"\n错误: {e}")
|
||||
567
bitmart/三分之一策略-5分钟交易.py
Normal file
567
bitmart/三分之一策略-5分钟交易.py
Normal file
@@ -0,0 +1,567 @@
|
||||
"""
|
||||
BitMart 三分之一策略 — 5分钟K线交易
|
||||
|
||||
策略规则:
|
||||
1. 触发价格(基于前一根有效K线实体>=0.1,使用当前K线开盘价):
|
||||
- 做多触发价 = 当前K线开盘价 + 实体/3
|
||||
- 做空触发价 = 当前K线开盘价 - 实体/3
|
||||
|
||||
2. 信号触发(无持仓时):
|
||||
- 当前5分钟K线最高价 >= 做多触发价 → 做多信号
|
||||
- 当前5分钟K线最低价 <= 做空触发价 → 做空信号
|
||||
|
||||
3. 执行逻辑:
|
||||
- 做多时遇到做空信号 -> 平多并反手开空
|
||||
- 做空时遇到做多信号 -> 平空并反手开多
|
||||
- 同一根5分钟K线内只交易一次
|
||||
|
||||
4. 同根K线多空都触及时,用开盘价与触发价距离判断先后。
|
||||
|
||||
5. 反手信号(有持仓时检测):
|
||||
- 反手做空:当前持多,上一根K线上阴线涨幅 > 0.01%,当前K线跌到上一根K线的实体下边 -> 平多反手开空
|
||||
- 反手做多:当前持空,上一根K线下阴线跌幅 > 0.01%,当前K线涨到上一根K线的实体上边 -> 平空反手开多
|
||||
(上阴线 = high - max(open,close),上阴线涨幅 = (上阴线/开盘价)*100;下阴线同理)
|
||||
(实体上边 = max(open,close),实体下边 = min(open,close))
|
||||
"""
|
||||
import random
|
||||
import time
|
||||
import datetime
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
from tqdm import tqdm
|
||||
from loguru import logger
|
||||
from bit_tools import openBrowser
|
||||
from DrissionPage import ChromiumPage
|
||||
from DrissionPage import ChromiumOptions
|
||||
|
||||
from bitmart.api_contract import APIContract
|
||||
|
||||
try:
|
||||
from 交易.tools import send_dingtalk_message
|
||||
except ImportError:
|
||||
send_dingtalk_message = None
|
||||
|
||||
ding_executor = ThreadPoolExecutor(max_workers=2, thread_name_prefix="dingtalk")
|
||||
|
||||
|
||||
class BitmartOneThirdStrategy:
|
||||
"""三分之一策略:触发价 = 当前K线开盘价 ± 实体/3,5分钟K线"""
|
||||
|
||||
def __init__(self, bit_id):
|
||||
self.page: ChromiumPage | None = None
|
||||
|
||||
self.api_key = "a0fb7b98464fd9bcce67e7c519d58ec10d0c38a8"
|
||||
self.secret_key = "4eaeba78e77aeaab1c2027f846a276d164f264a44c2c1bb1c5f3be50c8de1ca5"
|
||||
self.memo = "合约交易"
|
||||
|
||||
self.contract_symbol = "ETHUSDT"
|
||||
self.contractAPI = APIContract(self.api_key, self.secret_key, self.memo, timeout=(5, 15))
|
||||
|
||||
self.start = 0 # 持仓: -1 空, 0 无, 1 多
|
||||
self.direction = None
|
||||
self.pbar = tqdm(total=5, desc="等待K线", ncols=80)
|
||||
self.last_kline_time = None
|
||||
|
||||
self.leverage = "100"
|
||||
self.open_type = "cross"
|
||||
self.risk_percent = 0.01
|
||||
|
||||
self.open_avg_price = None
|
||||
self.current_amount = None
|
||||
self.position_cross = None
|
||||
self.bit_id = bit_id
|
||||
|
||||
self.min_body_size = 0.1 # 前一根有效K线实体下限
|
||||
self.kline_step = 5 # 5分钟K线
|
||||
self.check_interval = 3
|
||||
self.last_trigger_kline_id = None
|
||||
self.last_trigger_direction = None
|
||||
self.min_shadow_pct = 0.01 # 反手信号:上/下影线幅度需 > 0.01%
|
||||
|
||||
# ========================= 三分之一策略核心 =========================
|
||||
|
||||
def is_bullish(self, c):
|
||||
return float(c['close']) > float(c['open'])
|
||||
|
||||
def get_body_size(self, candle):
|
||||
return abs(float(candle['open']) - float(candle['close']))
|
||||
|
||||
def find_valid_prev_bar(self, all_data, current_idx):
|
||||
"""找到当前K线之前最近一根实体>=min_body_size的K线"""
|
||||
if current_idx <= 0:
|
||||
return None, None
|
||||
for i in range(current_idx - 1, -1, -1):
|
||||
prev = all_data[i]
|
||||
if self.get_body_size(prev) >= self.min_body_size:
|
||||
return i, prev
|
||||
return None, None
|
||||
|
||||
def get_one_third_levels(self, prev, curr):
|
||||
"""
|
||||
做多触发价 = 当前K线开盘价 + 实体/3
|
||||
做空触发价 = 当前K线开盘价 - 实体/3
|
||||
实体来自前一根有效K线
|
||||
"""
|
||||
p_open = float(prev['open'])
|
||||
p_close = float(prev['close'])
|
||||
curr_open = float(curr['open'])
|
||||
body = abs(p_open - p_close)
|
||||
if body < 0.001:
|
||||
return None, None
|
||||
long_trigger = curr_open + body / 3
|
||||
short_trigger = curr_open - body / 3
|
||||
return long_trigger, short_trigger
|
||||
|
||||
def get_upper_shadow(self, candle):
|
||||
"""上影线 = high - max(open, close)"""
|
||||
o, c, h = float(candle['open']), float(candle['close']), float(candle['high'])
|
||||
return h - max(o, c)
|
||||
|
||||
def get_lower_shadow(self, candle):
|
||||
"""下影线 = min(open, close) - low"""
|
||||
o, c, l = float(candle['open']), float(candle['close']), float(candle['low'])
|
||||
return min(o, c) - l
|
||||
|
||||
def upper_shadow_pct(self, candle):
|
||||
"""上影线涨幅 = (上影线/开盘价)*100"""
|
||||
o = float(candle['open'])
|
||||
if o <= 0:
|
||||
return 0.0
|
||||
return self.get_upper_shadow(candle) / o * 100
|
||||
|
||||
def lower_shadow_pct(self, candle):
|
||||
"""下影线跌幅 = (下影线/开盘价)*100"""
|
||||
o = float(candle['open'])
|
||||
if o <= 0:
|
||||
return 0.0
|
||||
return self.get_lower_shadow(candle) / o * 100
|
||||
|
||||
def get_body_upper_edge(self, candle):
|
||||
"""实体上边 = max(open, close)"""
|
||||
return max(float(candle['open']), float(candle['close']))
|
||||
|
||||
def get_body_lower_edge(self, candle):
|
||||
"""实体下边 = min(open, close)"""
|
||||
return min(float(candle['open']), float(candle['close']))
|
||||
|
||||
def check_reverse_signal(self, kline_data):
|
||||
"""
|
||||
反手信号(有持仓时检测):
|
||||
- 反手做空:当前持多,上一根K线上阴线涨幅 > 0.01%,当前K线跌到上一根K线的实体下边
|
||||
- 反手做多:当前持空,上一根K线下阴线跌幅 > 0.01%,当前K线涨到上一根K线的实体上边
|
||||
使用紧邻的上一根K线(kline_data[-2])。
|
||||
返回:(方向 'long'|'short', 上一根K线) 或 (None, None)
|
||||
"""
|
||||
if len(kline_data) < 2:
|
||||
return None, None
|
||||
curr = kline_data[-1]
|
||||
prev = kline_data[-2]
|
||||
curr_high = float(curr['high'])
|
||||
curr_low = float(curr['low'])
|
||||
prev_body_upper = self.get_body_upper_edge(prev) # 实体上边
|
||||
prev_body_lower = self.get_body_lower_edge(prev) # 实体下边
|
||||
|
||||
# 持多反手做空:上一根上阴线涨幅>0.01%,当前跌到上一根实体下边
|
||||
if self.start == 1:
|
||||
if self.upper_shadow_pct(prev) > self.min_shadow_pct and curr_low <= prev_body_lower:
|
||||
return 'short', prev
|
||||
# 持空反手做多:上一根下阴线跌幅>0.01%,当前涨到上一根实体上边
|
||||
if self.start == -1:
|
||||
if self.lower_shadow_pct(prev) > self.min_shadow_pct and curr_high >= prev_body_upper:
|
||||
return 'long', prev
|
||||
return None, None
|
||||
|
||||
def check_realtime_trigger(self, kline_data):
|
||||
"""
|
||||
检查当前5分钟K线是否触发信号。
|
||||
做多触发价 = 当前K线开盘价 + 实体/3,做空 = 当前K线开盘价 - 实体/3。
|
||||
同根多空都触发时用开盘价与触发价距离判断先后。
|
||||
返回:(方向, 触发价格, 有效前一根K线, 当前K线) 或 (None, None, None, None)
|
||||
"""
|
||||
if len(kline_data) < 2:
|
||||
return None, None, None, None
|
||||
|
||||
curr = kline_data[-1]
|
||||
curr_kline_id = curr['id']
|
||||
curr_high = float(curr['high'])
|
||||
curr_low = float(curr['low'])
|
||||
curr_open = float(curr['open'])
|
||||
|
||||
valid_prev_idx, prev = self.find_valid_prev_bar(kline_data, len(kline_data) - 1)
|
||||
if prev is None:
|
||||
return None, None, None, None
|
||||
|
||||
long_trigger, short_trigger = self.get_one_third_levels(prev, curr)
|
||||
if long_trigger is None:
|
||||
return None, None, None, None
|
||||
|
||||
long_triggered = curr_high >= long_trigger
|
||||
short_triggered = curr_low <= short_trigger
|
||||
both_triggered = long_triggered and short_triggered
|
||||
|
||||
direction = None
|
||||
trigger_price = None
|
||||
|
||||
if both_triggered:
|
||||
dist_to_long = abs(long_trigger - curr_open)
|
||||
dist_to_short = abs(short_trigger - curr_open)
|
||||
if dist_to_short <= dist_to_long:
|
||||
direction = 'short'
|
||||
trigger_price = short_trigger
|
||||
else:
|
||||
direction = 'long'
|
||||
trigger_price = long_trigger
|
||||
elif short_triggered:
|
||||
direction = 'short'
|
||||
trigger_price = short_trigger
|
||||
elif long_triggered:
|
||||
direction = 'long'
|
||||
trigger_price = long_trigger
|
||||
|
||||
if direction is None:
|
||||
return None, None, None, None
|
||||
|
||||
if self.last_trigger_kline_id == curr_kline_id and self.last_trigger_direction == direction:
|
||||
return None, None, None, None
|
||||
|
||||
return direction, trigger_price, prev, curr
|
||||
|
||||
# ========================= API =========================
|
||||
|
||||
def get_klines(self):
|
||||
"""获取最近5分钟K线"""
|
||||
try:
|
||||
end_time = int(time.time())
|
||||
response = self.contractAPI.get_kline(
|
||||
contract_symbol=self.contract_symbol,
|
||||
step=self.kline_step,
|
||||
start_time=end_time - 3600 * 3,
|
||||
end_time=end_time
|
||||
)[0]["data"]
|
||||
formatted = []
|
||||
for k in response:
|
||||
formatted.append({
|
||||
'id': int(k["timestamp"]),
|
||||
'open': float(k["open_price"]),
|
||||
'high': float(k["high_price"]),
|
||||
'low': float(k["low_price"]),
|
||||
'close': float(k["close_price"])
|
||||
})
|
||||
formatted.sort(key=lambda x: x['id'])
|
||||
return formatted
|
||||
except Exception as e:
|
||||
if "429" in str(e) or "too many requests" in str(e).lower():
|
||||
logger.warning(f"API限流,等待60秒: {e}")
|
||||
time.sleep(60)
|
||||
else:
|
||||
logger.error(f"获取K线异常: {e}")
|
||||
self.ding("获取K线异常", error=True)
|
||||
return None
|
||||
|
||||
def get_current_price(self):
|
||||
try:
|
||||
end_time = int(time.time())
|
||||
response = self.contractAPI.get_kline(
|
||||
contract_symbol=self.contract_symbol,
|
||||
step=1,
|
||||
start_time=end_time - 3600 * 3,
|
||||
end_time=end_time
|
||||
)[0]
|
||||
if response.get('code') == 1000:
|
||||
return float(response['data'][-1]["close_price"])
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"获取价格异常: {e}")
|
||||
return None
|
||||
|
||||
def get_available_balance(self):
|
||||
try:
|
||||
response = self.contractAPI.get_assets_detail()[0]
|
||||
if response.get('code') == 1000:
|
||||
data = response.get('data')
|
||||
if isinstance(data, dict):
|
||||
return float(data.get('available_balance', 0))
|
||||
if isinstance(data, list):
|
||||
for asset in data:
|
||||
if asset.get('currency') == 'USDT':
|
||||
return float(asset.get('available_balance', 0))
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"余额查询异常: {e}")
|
||||
return None
|
||||
|
||||
def get_position_status(self):
|
||||
try:
|
||||
response = self.contractAPI.get_position(contract_symbol=self.contract_symbol)[0]
|
||||
if response.get('code') == 1000:
|
||||
positions = response.get('data') or []
|
||||
if not positions:
|
||||
self.start = 0
|
||||
self.open_avg_price = None
|
||||
self.current_amount = None
|
||||
self.position_cross = None
|
||||
return True
|
||||
self.start = 1 if positions[0].get('position_type') == 1 else -1
|
||||
self.open_avg_price = positions[0].get('open_avg_price')
|
||||
self.current_amount = positions[0].get('current_amount')
|
||||
self.position_cross = positions[0].get("position_cross")
|
||||
return True
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"持仓查询异常: {e}")
|
||||
return False
|
||||
|
||||
def set_leverage(self):
|
||||
try:
|
||||
response = self.contractAPI.post_submit_leverage(
|
||||
contract_symbol=self.contract_symbol,
|
||||
leverage=self.leverage,
|
||||
open_type=self.open_type
|
||||
)[0]
|
||||
if response.get('code') == 1000:
|
||||
logger.success(f"全仓 {self.leverage}x 杠杆设置成功")
|
||||
return True
|
||||
logger.error(f"杠杆设置失败: {response}")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"设置杠杆异常: {e}")
|
||||
return False
|
||||
|
||||
# ========================= 浏览器 =========================
|
||||
|
||||
def open_browser(self):
|
||||
try:
|
||||
bit_port = openBrowser(id=self.bit_id)
|
||||
co = ChromiumOptions()
|
||||
co.set_local_port(port=bit_port)
|
||||
self.page = ChromiumPage(addr_or_opts=co)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def close_extra_tabs(self):
|
||||
try:
|
||||
for idx, tab in enumerate(self.page.get_tabs()):
|
||||
if idx > 0:
|
||||
tab.close()
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def click_safe(self, xpath, sleep=0.5):
|
||||
try:
|
||||
ele = self.page.ele(xpath)
|
||||
if not ele:
|
||||
return False
|
||||
ele.scroll.to_see(center=True)
|
||||
time.sleep(sleep)
|
||||
ele.click(by_js=True)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def 平仓(self):
|
||||
logger.info("执行平仓...")
|
||||
self.click_safe('x://span[normalize-space(text()) ="市价"]')
|
||||
time.sleep(0.5)
|
||||
self.ding("执行平仓")
|
||||
|
||||
def 开单(self, marketPriceLongOrder=0, size=None):
|
||||
if size is None or size <= 0:
|
||||
logger.warning("开单金额无效")
|
||||
return False
|
||||
direction_str = "做多" if marketPriceLongOrder == 1 else "做空"
|
||||
logger.info(f"执行{direction_str},金额: {size}")
|
||||
try:
|
||||
if marketPriceLongOrder == -1:
|
||||
self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
self.page.ele('x://*[@id="size_0"]').input(size)
|
||||
self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]')
|
||||
elif marketPriceLongOrder == 1:
|
||||
self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
self.page.ele('x://*[@id="size_0"]').input(size)
|
||||
self.click_safe('x://span[normalize-space(text()) ="买入/做多"]')
|
||||
self.ding(f"执行{direction_str},金额: {size}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"开单异常: {e}")
|
||||
return False
|
||||
|
||||
def ding(self, msg, error=False):
|
||||
prefix = "❌三分之一策略:" if error else "🔔三分之一策略:"
|
||||
full_msg = f"{prefix}{msg}"
|
||||
if error:
|
||||
logger.error(msg)
|
||||
for _ in range(3):
|
||||
ding_executor.submit(self._send_ding_safe, full_msg)
|
||||
else:
|
||||
logger.info(msg)
|
||||
ding_executor.submit(self._send_ding_safe, full_msg)
|
||||
|
||||
def _send_ding_safe(self, msg):
|
||||
try:
|
||||
if send_dingtalk_message:
|
||||
send_dingtalk_message(msg)
|
||||
except Exception as e:
|
||||
logger.warning(f"钉钉发送失败: {e}")
|
||||
|
||||
# ========================= 主循环 =========================
|
||||
|
||||
def action(self):
|
||||
if not self.set_leverage():
|
||||
logger.error("杠杆设置失败,程序继续运行但可能下单失败")
|
||||
if not self.open_browser():
|
||||
self.ding("打开浏览器失败!", error=True)
|
||||
return
|
||||
logger.info("浏览器打开成功")
|
||||
self.close_extra_tabs()
|
||||
self.page.get("https://derivatives.bitmart.com/zh-CN/futures/ETHUSDT")
|
||||
time.sleep(2)
|
||||
self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
logger.info("三分之一策略(5分钟K线)开始监测")
|
||||
|
||||
last_report_time = 0
|
||||
report_interval = 300
|
||||
|
||||
while True:
|
||||
for _ in range(5):
|
||||
if self.open_browser():
|
||||
break
|
||||
time.sleep(5)
|
||||
else:
|
||||
self.ding("打开浏览器失败!", error=True)
|
||||
return
|
||||
|
||||
self.close_extra_tabs()
|
||||
self.page.get("https://derivatives.bitmart.com/zh-CN/futures/ETHUSDT")
|
||||
time.sleep(2)
|
||||
self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
|
||||
try:
|
||||
kline_data = self.get_klines()
|
||||
if not kline_data or len(kline_data) < 2:
|
||||
logger.warning("K线数据不足,等待重试...")
|
||||
time.sleep(self.check_interval)
|
||||
continue
|
||||
|
||||
curr = kline_data[-1]
|
||||
prev = kline_data[-2]
|
||||
curr_time_str = datetime.datetime.fromtimestamp(curr['id']).strftime('%H:%M:%S')
|
||||
|
||||
if not self.get_position_status():
|
||||
logger.warning("获取仓位失败,使用缓存")
|
||||
|
||||
curr_kline_id = curr['id']
|
||||
|
||||
# 有持仓时优先检测反手信号
|
||||
if self.start != 0:
|
||||
rev_dir, rev_prev = self.check_reverse_signal(kline_data)
|
||||
if rev_dir:
|
||||
balance = self.get_available_balance()
|
||||
trade_size = (balance or 0) * self.risk_percent
|
||||
if rev_prev is not None:
|
||||
up_pct = self.upper_shadow_pct(rev_prev)
|
||||
low_pct = self.lower_shadow_pct(rev_prev)
|
||||
prev_body_upper = self.get_body_upper_edge(rev_prev)
|
||||
prev_body_lower = self.get_body_lower_edge(rev_prev)
|
||||
logger.info(f"{'='*50}")
|
||||
if rev_dir == 'long':
|
||||
logger.info(f"🔄 反手信号({rev_dir}):当前K线涨到上一根实体上边={prev_body_upper:.2f}")
|
||||
else:
|
||||
logger.info(f"🔄 反手信号({rev_dir}):当前K线跌到上一根实体下边={prev_body_lower:.2f}")
|
||||
logger.info(f" 上一根 上阴线涨幅={up_pct:.3f}% 下阴线跌幅={low_pct:.3f}%")
|
||||
logger.info(f"{'='*50}")
|
||||
if rev_dir == 'long':
|
||||
logger.info("📈 平空反手开多")
|
||||
self.平仓()
|
||||
time.sleep(1)
|
||||
self.开单(marketPriceLongOrder=1, size=trade_size)
|
||||
else:
|
||||
logger.info("📉 平多反手开空")
|
||||
self.平仓()
|
||||
time.sleep(1)
|
||||
self.开单(marketPriceLongOrder=-1, size=trade_size)
|
||||
self.last_trigger_kline_id = curr_kline_id
|
||||
self.last_trigger_direction = rev_dir
|
||||
self.get_position_status()
|
||||
time.sleep(self.check_interval)
|
||||
continue
|
||||
|
||||
direction, trigger_price, valid_prev, curr_kline = self.check_realtime_trigger(kline_data)
|
||||
|
||||
if direction:
|
||||
curr_kline_id = curr_kline['id']
|
||||
|
||||
# 信号与持仓同向则不操作
|
||||
if (direction == "long" and self.start == 1) or (direction == "short" and self.start == -1):
|
||||
self.last_trigger_kline_id = curr_kline_id
|
||||
self.last_trigger_direction = direction
|
||||
time.sleep(self.check_interval)
|
||||
continue
|
||||
|
||||
prev_time = datetime.datetime.fromtimestamp(valid_prev['id']).strftime('%H:%M')
|
||||
prev_type = "阳线" if self.is_bullish(valid_prev) else "阴线"
|
||||
prev_body = self.get_body_size(valid_prev)
|
||||
|
||||
logger.info(f"{'='*50}")
|
||||
logger.info(f"🚨 检测到{direction}信号 触发价={trigger_price:.2f}")
|
||||
logger.info(f" 有效前一根[{prev_time}] {prev_type} 实体={prev_body:.2f}")
|
||||
logger.info(f" 当前5分钟K线 O={curr_kline['open']:.2f} H={curr_kline['high']:.2f} L={curr_kline['low']:.2f} C={curr_kline['close']:.2f}")
|
||||
logger.info(f" 当前持仓: {self.start} (1=多 -1=空 0=无)")
|
||||
|
||||
balance = self.get_available_balance()
|
||||
trade_size = (balance or 0) * self.risk_percent
|
||||
executed = False
|
||||
|
||||
if direction == "long":
|
||||
if self.start == -1:
|
||||
logger.info("📈 平空反手开多")
|
||||
self.平仓()
|
||||
time.sleep(1)
|
||||
self.开单(marketPriceLongOrder=1, size=trade_size)
|
||||
executed = True
|
||||
elif self.start == 0:
|
||||
logger.info("📈 无仓位开多")
|
||||
self.开单(marketPriceLongOrder=1, size=trade_size)
|
||||
executed = True
|
||||
elif direction == "short":
|
||||
if self.start == 1:
|
||||
logger.info("📉 平多反手开空")
|
||||
self.平仓()
|
||||
time.sleep(1)
|
||||
self.开单(marketPriceLongOrder=-1, size=trade_size)
|
||||
executed = True
|
||||
elif self.start == 0:
|
||||
logger.info("📉 无仓位开空")
|
||||
self.开单(marketPriceLongOrder=-1, size=trade_size)
|
||||
executed = True
|
||||
|
||||
self.last_trigger_kline_id = curr_kline_id
|
||||
self.last_trigger_direction = direction
|
||||
if executed:
|
||||
self.get_position_status()
|
||||
logger.info(f"{'='*50}")
|
||||
else:
|
||||
logger.debug(f"[{curr_time_str}] O={curr['open']:.2f} H={curr['high']:.2f} L={curr['low']:.2f}")
|
||||
|
||||
if time.time() - last_report_time >= report_interval:
|
||||
last_report_time = time.time()
|
||||
time.sleep(self.check_interval)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"主循环异常: {e}")
|
||||
time.sleep(self.check_interval)
|
||||
time.sleep(3)
|
||||
|
||||
if random.randint(1, 10) > 7:
|
||||
self.page.close()
|
||||
time.sleep(15)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
BitmartOneThirdStrategy(bit_id="f2320f57e24c45529a009e1541e25961").action()
|
||||
except KeyboardInterrupt:
|
||||
logger.info("程序被用户中断")
|
||||
finally:
|
||||
ding_executor.shutdown(wait=True)
|
||||
516
bitmart/五分之一策略-开盘价版-3分钟交易.py
Normal file
516
bitmart/五分之一策略-开盘价版-3分钟交易.py
Normal file
@@ -0,0 +1,516 @@
|
||||
"""
|
||||
BitMart 五分之一策略(开盘价版)— 3分钟K线交易
|
||||
|
||||
策略规则(与 1111 一致):
|
||||
基于前一根有效 K 线(实体 ≥ 0.1):
|
||||
做多触发价 = 当前K线开盘价 + 实体/5(收盘价向上 1/5 实体)
|
||||
做空触发价 = 当前K线开盘价 - 实体/5(收盘价向下 1/5 实体)
|
||||
|
||||
反手(若已有持仓):
|
||||
持空反手做多:价格涨到 开仓价 + 前一根实体/5
|
||||
持多反手做空:价格跌到 开仓价 - 前一根实体/5
|
||||
|
||||
同根K线内多空都触及时,用开盘价与触发价距离判断先后。
|
||||
"""
|
||||
import random
|
||||
import time
|
||||
import datetime
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
from tqdm import tqdm
|
||||
from loguru import logger
|
||||
from bit_tools import openBrowser
|
||||
from DrissionPage import ChromiumPage
|
||||
from DrissionPage import ChromiumOptions
|
||||
|
||||
from bitmart.api_contract import APIContract
|
||||
|
||||
try:
|
||||
from 交易.tools import send_dingtalk_message
|
||||
except ImportError:
|
||||
send_dingtalk_message = None
|
||||
|
||||
ding_executor = ThreadPoolExecutor(max_workers=2, thread_name_prefix="dingtalk")
|
||||
|
||||
|
||||
class BitmartOneFifthOpenStrategy:
|
||||
"""五分之一策略(开盘价版):触发价 = 当前K线开盘价 ± 实体/5,3分钟K线"""
|
||||
|
||||
def __init__(self, bit_id):
|
||||
self.page: ChromiumPage | None = None
|
||||
|
||||
self.api_key = "a0fb7b98464fd9bcce67e7c519d58ec10d0c38a8"
|
||||
self.secret_key = "4eaeba78e77aeaab1c2027f846a276d164f264a44c2c1bb1c5f3be50c8de1ca5"
|
||||
self.memo = "合约交易"
|
||||
|
||||
self.contract_symbol = "ETHUSDT"
|
||||
self.contractAPI = APIContract(self.api_key, self.secret_key, self.memo, timeout=(5, 15))
|
||||
|
||||
self.start = 0 # 持仓: -1 空, 0 无, 1 多
|
||||
self.direction = None
|
||||
self.pbar = tqdm(total=3, desc="等待K线", ncols=80)
|
||||
self.last_kline_time = None
|
||||
|
||||
self.leverage = "100"
|
||||
self.open_type = "cross"
|
||||
self.risk_percent = 0.01
|
||||
|
||||
self.open_avg_price = None
|
||||
self.current_amount = None
|
||||
self.bit_id = bit_id
|
||||
|
||||
self.min_body_size = 0.1 # 前一根有效K线实体下限
|
||||
self.kline_step = 3 # 3分钟K线
|
||||
self.check_interval = 3
|
||||
self.last_trigger_kline_id = None
|
||||
self.last_trigger_direction = None
|
||||
self.last_trade_kline_id = None
|
||||
|
||||
# ========================= 策略核心(开盘价版)=========================
|
||||
|
||||
def get_body_size(self, candle):
|
||||
return abs(float(candle['open']) - float(candle['close']))
|
||||
|
||||
def find_valid_prev_bar(self, all_data, current_idx):
|
||||
"""找到当前K线之前最近一根实体≥min_body_size的K线"""
|
||||
if current_idx <= 0:
|
||||
return None, None
|
||||
for i in range(current_idx - 1, -1, -1):
|
||||
prev = all_data[i]
|
||||
if self.get_body_size(prev) >= self.min_body_size:
|
||||
return i, prev
|
||||
return None, None
|
||||
|
||||
def get_trigger_levels_by_open(self, curr_open, prev_bar):
|
||||
"""
|
||||
做多触发价 = 当前K线开盘价 + 实体/5
|
||||
做空触发价 = 当前K线开盘价 - 实体/5
|
||||
实体来自前一根有效K线。
|
||||
"""
|
||||
body = self.get_body_size(prev_bar)
|
||||
if body < 0.001:
|
||||
return None, None
|
||||
curr_open_f = float(curr_open)
|
||||
long_trigger = curr_open_f + body / 5
|
||||
short_trigger = curr_open_f - body / 5
|
||||
return long_trigger, short_trigger
|
||||
|
||||
def check_realtime_trigger(self, kline_data):
|
||||
"""
|
||||
检查当前3分钟K线是否触发信号(开盘价版)。
|
||||
做多触发价 = 当前K线开盘价 + 前一根实体/5
|
||||
做空触发价 = 当前K线开盘价 - 前一根实体/5
|
||||
若同根多空都触发,用开盘价与触发价距离判断先后。
|
||||
返回:(方向, 触发价格, 有效前一根K线, 当前K线) 或 (None, None, None, None)
|
||||
"""
|
||||
if len(kline_data) < 2:
|
||||
return None, None, None, None
|
||||
|
||||
curr = kline_data[-1]
|
||||
curr_kline_id = curr['id']
|
||||
curr_open = float(curr['open'])
|
||||
curr_high = float(curr['high'])
|
||||
curr_low = float(curr['low'])
|
||||
|
||||
valid_prev_idx, prev = self.find_valid_prev_bar(kline_data, len(kline_data) - 1)
|
||||
if prev is None:
|
||||
return None, None, None, None
|
||||
|
||||
long_trigger, short_trigger = self.get_trigger_levels_by_open(curr_open, prev)
|
||||
if long_trigger is None:
|
||||
return None, None, None, None
|
||||
|
||||
long_triggered = curr_high >= long_trigger
|
||||
short_triggered = curr_low <= short_trigger
|
||||
both_triggered = long_triggered and short_triggered
|
||||
|
||||
direction = None
|
||||
trigger_price = None
|
||||
|
||||
if both_triggered:
|
||||
dist_to_long = abs(long_trigger - curr_open)
|
||||
dist_to_short = abs(short_trigger - curr_open)
|
||||
if dist_to_short <= dist_to_long:
|
||||
direction = 'short'
|
||||
trigger_price = short_trigger
|
||||
else:
|
||||
direction = 'long'
|
||||
trigger_price = long_trigger
|
||||
elif short_triggered:
|
||||
direction = 'short'
|
||||
trigger_price = short_trigger
|
||||
elif long_triggered:
|
||||
direction = 'long'
|
||||
trigger_price = long_trigger
|
||||
|
||||
if direction is None:
|
||||
return None, None, None, None
|
||||
|
||||
if self.last_trigger_kline_id == curr_kline_id and self.last_trigger_direction == direction:
|
||||
return None, None, None, None
|
||||
|
||||
return direction, trigger_price, prev, curr
|
||||
|
||||
def check_reverse_trigger(self, kline_data):
|
||||
"""
|
||||
反手检测:持空反手做多 = 现价 >= 开仓价+前一根实体/5
|
||||
持多反手做空 = 现价 <= 开仓价-前一根实体/5
|
||||
使用当前价(最新K线收盘或 get_current_price)与 开仓价±实体/5 比较。
|
||||
"""
|
||||
if self.start == 0 or self.open_avg_price is None:
|
||||
return None, None
|
||||
if len(kline_data) < 2:
|
||||
return None, None
|
||||
|
||||
valid_prev_idx, prev = self.find_valid_prev_bar(kline_data, len(kline_data) - 1)
|
||||
if prev is None:
|
||||
return None, None
|
||||
|
||||
body = self.get_body_size(prev)
|
||||
if body < 0.001:
|
||||
return None, None
|
||||
|
||||
open_price_f = float(self.open_avg_price)
|
||||
reverse_long_price = open_price_f + body / 5 # 持空反手做多线
|
||||
reverse_short_price = open_price_f - body / 5 # 持多反手做空线
|
||||
|
||||
curr = kline_data[-1]
|
||||
curr_high = float(curr['high'])
|
||||
curr_low = float(curr['low'])
|
||||
|
||||
if self.start == -1 and curr_high >= reverse_long_price:
|
||||
return 'long', reverse_long_price
|
||||
if self.start == 1 and curr_low <= reverse_short_price:
|
||||
return 'short', reverse_short_price
|
||||
return None, None
|
||||
|
||||
def is_bullish(self, c):
|
||||
return float(c['close']) > float(c['open'])
|
||||
|
||||
# ========================= API =========================
|
||||
|
||||
def get_klines(self):
|
||||
"""获取最近3分钟K线"""
|
||||
try:
|
||||
end_time = int(time.time())
|
||||
response = self.contractAPI.get_kline(
|
||||
contract_symbol=self.contract_symbol,
|
||||
step=self.kline_step,
|
||||
start_time=end_time - 3600 * 3,
|
||||
end_time=end_time
|
||||
)[0]["data"]
|
||||
formatted = []
|
||||
for k in response:
|
||||
formatted.append({
|
||||
'id': int(k["timestamp"]),
|
||||
'open': float(k["open_price"]),
|
||||
'high': float(k["high_price"]),
|
||||
'low': float(k["low_price"]),
|
||||
'close': float(k["close_price"])
|
||||
})
|
||||
formatted.sort(key=lambda x: x['id'])
|
||||
return formatted
|
||||
except Exception as e:
|
||||
if "429" in str(e) or "too many requests" in str(e).lower():
|
||||
logger.warning(f"API限流,等待60秒: {e}")
|
||||
time.sleep(60)
|
||||
else:
|
||||
logger.error(f"获取K线异常: {e}")
|
||||
self.ding("获取K线异常", error=True)
|
||||
return None
|
||||
|
||||
def get_current_price(self):
|
||||
try:
|
||||
end_time = int(time.time())
|
||||
response = self.contractAPI.get_kline(
|
||||
contract_symbol=self.contract_symbol,
|
||||
step=1,
|
||||
start_time=end_time - 3600 * 3,
|
||||
end_time=end_time
|
||||
)[0]
|
||||
if response.get('code') == 1000:
|
||||
return float(response['data'][-1]["close_price"])
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"获取价格异常: {e}")
|
||||
return None
|
||||
|
||||
def get_available_balance(self):
|
||||
try:
|
||||
response = self.contractAPI.get_assets_detail()[0]
|
||||
if response.get('code') == 1000:
|
||||
data = response.get('data')
|
||||
if isinstance(data, dict):
|
||||
return float(data.get('available_balance', 0))
|
||||
if isinstance(data, list):
|
||||
for asset in data:
|
||||
if asset.get('currency') == 'USDT':
|
||||
return float(asset.get('available_balance', 0))
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"余额查询异常: {e}")
|
||||
return None
|
||||
|
||||
def get_position_status(self):
|
||||
try:
|
||||
response = self.contractAPI.get_position(contract_symbol=self.contract_symbol)[0]
|
||||
if response.get('code') == 1000:
|
||||
positions = response.get('data') or []
|
||||
if not positions:
|
||||
self.start = 0
|
||||
self.open_avg_price = None
|
||||
self.current_amount = None
|
||||
self.position_cross = None
|
||||
return True
|
||||
self.start = 1 if positions[0].get('position_type') == 1 else -1
|
||||
self.open_avg_price = positions[0].get('open_avg_price')
|
||||
self.current_amount = positions[0].get('current_amount')
|
||||
self.position_cross = positions[0].get("position_cross")
|
||||
return True
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"持仓查询异常: {e}")
|
||||
return False
|
||||
|
||||
def set_leverage(self):
|
||||
try:
|
||||
response = self.contractAPI.post_submit_leverage(
|
||||
contract_symbol=self.contract_symbol,
|
||||
leverage=self.leverage,
|
||||
open_type=self.open_type
|
||||
)[0]
|
||||
if response.get('code') == 1000:
|
||||
logger.success(f"全仓 {self.leverage}x 杠杆设置成功")
|
||||
return True
|
||||
logger.error(f"杠杆设置失败: {response}")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"设置杠杆异常: {e}")
|
||||
return False
|
||||
|
||||
# ========================= 浏览器 =========================
|
||||
|
||||
def open_browser(self):
|
||||
try:
|
||||
bit_port = openBrowser(id=self.bit_id)
|
||||
co = ChromiumOptions()
|
||||
co.set_local_port(port=bit_port)
|
||||
self.page = ChromiumPage(addr_or_opts=co)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def close_extra_tabs(self):
|
||||
try:
|
||||
for idx, tab in enumerate(self.page.get_tabs()):
|
||||
if idx > 0:
|
||||
tab.close()
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def click_safe(self, xpath, sleep=0.5):
|
||||
try:
|
||||
ele = self.page.ele(xpath)
|
||||
if not ele:
|
||||
return False
|
||||
ele.scroll.to_see(center=True)
|
||||
time.sleep(sleep)
|
||||
ele.click(by_js=True)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def 平仓(self):
|
||||
logger.info("执行平仓...")
|
||||
self.click_safe('x://span[normalize-space(text()) ="市价"]')
|
||||
time.sleep(0.5)
|
||||
self.ding("执行平仓")
|
||||
|
||||
def 开单(self, marketPriceLongOrder=0, size=None):
|
||||
if size is None or size <= 0:
|
||||
logger.warning("开单金额无效")
|
||||
return False
|
||||
direction_str = "做多" if marketPriceLongOrder == 1 else "做空"
|
||||
logger.info(f"执行{direction_str},金额: {size}")
|
||||
try:
|
||||
if marketPriceLongOrder == -1:
|
||||
self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
self.page.ele('x://*[@id="size_0"]').input(size)
|
||||
self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]')
|
||||
elif marketPriceLongOrder == 1:
|
||||
self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
self.page.ele('x://*[@id="size_0"]').input(size)
|
||||
self.click_safe('x://span[normalize-space(text()) ="买入/做多"]')
|
||||
self.ding(f"执行{direction_str},金额: {size}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"开单异常: {e}")
|
||||
return False
|
||||
|
||||
def ding(self, msg, error=False):
|
||||
prefix = "❌五分之一开盘价版:" if error else "🔔五分之一开盘价版:"
|
||||
full_msg = f"{prefix}{msg}"
|
||||
if error:
|
||||
logger.error(msg)
|
||||
for _ in range(3):
|
||||
ding_executor.submit(self._send_ding_safe, full_msg)
|
||||
else:
|
||||
logger.info(msg)
|
||||
ding_executor.submit(self._send_ding_safe, full_msg)
|
||||
|
||||
def _send_ding_safe(self, msg):
|
||||
try:
|
||||
if send_dingtalk_message:
|
||||
send_dingtalk_message(msg)
|
||||
except Exception as e:
|
||||
logger.warning(f"钉钉发送失败: {e}")
|
||||
|
||||
# ========================= 主循环 =========================
|
||||
|
||||
def action(self):
|
||||
if not self.set_leverage():
|
||||
logger.error("杠杆设置失败,程序继续运行但可能下单失败")
|
||||
if not self.open_browser():
|
||||
self.ding("打开浏览器失败!", error=True)
|
||||
return
|
||||
logger.info("浏览器打开成功")
|
||||
self.close_extra_tabs()
|
||||
self.page.get("https://derivatives.bitmart.com/zh-CN/futures/ETHUSDT")
|
||||
time.sleep(2)
|
||||
self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
logger.info("五分之一策略(开盘价版,3分钟K线)开始监测")
|
||||
|
||||
last_report_time = 0
|
||||
report_interval = 300
|
||||
|
||||
while True:
|
||||
for _ in range(5):
|
||||
if self.open_browser():
|
||||
break
|
||||
time.sleep(5)
|
||||
else:
|
||||
self.ding("打开浏览器失败!", error=True)
|
||||
return
|
||||
|
||||
self.close_extra_tabs()
|
||||
self.page.get("https://derivatives.bitmart.com/zh-CN/futures/ETHUSDT")
|
||||
time.sleep(2)
|
||||
self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
|
||||
try:
|
||||
kline_data = self.get_klines()
|
||||
if not kline_data or len(kline_data) < 3:
|
||||
logger.warning("K线数据不足,等待重试...")
|
||||
time.sleep(self.check_interval)
|
||||
continue
|
||||
|
||||
curr = kline_data[-1]
|
||||
curr_time_str = datetime.datetime.fromtimestamp(curr['id']).strftime('%H:%M:%S')
|
||||
|
||||
if not self.get_position_status():
|
||||
logger.warning("获取仓位失败,使用缓存")
|
||||
|
||||
# 1) 反手检测:有持仓时先看是否触发反手
|
||||
rev_dir, rev_price = self.check_reverse_trigger(kline_data)
|
||||
if rev_dir:
|
||||
curr_kline_id = curr['id']
|
||||
if self.last_trade_kline_id != curr_kline_id:
|
||||
prev_idx, prev_bar = self.find_valid_prev_bar(kline_data, len(kline_data) - 1)
|
||||
if prev_bar is not None:
|
||||
balance = self.get_available_balance()
|
||||
trade_size = (balance or 0) * self.risk_percent
|
||||
logger.info(f"反手信号: {rev_dir} 触发价={rev_price:.2f}")
|
||||
if rev_dir == 'long':
|
||||
self.平仓()
|
||||
time.sleep(1)
|
||||
self.开单(marketPriceLongOrder=1, size=trade_size)
|
||||
else:
|
||||
self.平仓()
|
||||
time.sleep(1)
|
||||
self.开单(marketPriceLongOrder=-1, size=trade_size)
|
||||
self.last_trade_kline_id = curr_kline_id
|
||||
self.last_trigger_kline_id = curr_kline_id
|
||||
self.last_trigger_direction = rev_dir
|
||||
self.get_position_status()
|
||||
time.sleep(self.check_interval)
|
||||
continue
|
||||
|
||||
# 2) 常规开仓/反手:基于 当前K线开盘价 ± 实体/5
|
||||
direction, trigger_price, valid_prev, curr_kline = self.check_realtime_trigger(kline_data)
|
||||
|
||||
if direction:
|
||||
curr_kline_id = curr_kline['id']
|
||||
if self.last_trade_kline_id == curr_kline_id:
|
||||
self.last_trigger_kline_id = curr_kline_id
|
||||
self.last_trigger_direction = direction
|
||||
time.sleep(self.check_interval)
|
||||
continue
|
||||
|
||||
prev_time = datetime.datetime.fromtimestamp(valid_prev['id']).strftime('%H:%M')
|
||||
prev_type = "阳线" if self.is_bullish(valid_prev) else "阴线"
|
||||
prev_body = self.get_body_size(valid_prev)
|
||||
|
||||
logger.info(f"{'='*50}")
|
||||
logger.info(f"🚨 检测到{direction}信号 触发价={trigger_price:.2f}")
|
||||
logger.info(f" 有效前一根[{prev_time}] {prev_type} 实体={prev_body:.2f}")
|
||||
logger.info(f" 当前3分钟K线 O={curr_kline['open']:.2f} H={curr_kline['high']:.2f} L={curr_kline['low']:.2f}")
|
||||
logger.info(f" 当前持仓: {self.start} (1=多 -1=空 0=无)")
|
||||
|
||||
balance = self.get_available_balance()
|
||||
trade_size = (balance or 0) * self.risk_percent
|
||||
executed = False
|
||||
|
||||
if direction == "long":
|
||||
if self.start == -1:
|
||||
logger.info("📈 平空反手开多")
|
||||
self.平仓()
|
||||
time.sleep(1)
|
||||
self.开单(marketPriceLongOrder=1, size=trade_size)
|
||||
executed = True
|
||||
elif self.start == 0:
|
||||
logger.info("📈 无仓位开多")
|
||||
self.开单(marketPriceLongOrder=1, size=trade_size)
|
||||
executed = True
|
||||
elif direction == "short":
|
||||
if self.start == 1:
|
||||
logger.info("📉 平多反手开空")
|
||||
self.平仓()
|
||||
time.sleep(1)
|
||||
self.开单(marketPriceLongOrder=-1, size=trade_size)
|
||||
executed = True
|
||||
elif self.start == 0:
|
||||
logger.info("📉 无仓位开空")
|
||||
self.开单(marketPriceLongOrder=-1, size=trade_size)
|
||||
executed = True
|
||||
|
||||
self.last_trigger_kline_id = curr_kline_id
|
||||
self.last_trigger_direction = direction
|
||||
if executed:
|
||||
self.last_trade_kline_id = curr_kline_id
|
||||
self.get_position_status()
|
||||
logger.info(f"{'='*50}")
|
||||
else:
|
||||
logger.debug(f"[{curr_time_str}] O={curr['open']:.2f} H={curr['high']:.2f} L={curr['low']:.2f}")
|
||||
|
||||
if time.time() - last_report_time >= report_interval:
|
||||
last_report_time = time.time()
|
||||
time.sleep(self.check_interval)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"主循环异常: {e}")
|
||||
time.sleep(self.check_interval)
|
||||
time.sleep(3)
|
||||
|
||||
if random.randint(1, 10) > 7:
|
||||
self.page.close()
|
||||
time.sleep(15)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
BitmartOneFifthOpenStrategy(bit_id="f2320f57e24c45529a009e1541e25961").action()
|
||||
except KeyboardInterrupt:
|
||||
logger.info("程序被用户中断")
|
||||
finally:
|
||||
ding_executor.shutdown(wait=True)
|
||||
686
bitmart/四分之一,五分钟,反手条件充足.py
Normal file
686
bitmart/四分之一,五分钟,反手条件充足.py
Normal file
@@ -0,0 +1,686 @@
|
||||
import time
|
||||
|
||||
from tqdm import tqdm
|
||||
from loguru import logger
|
||||
from bit_tools import openBrowser
|
||||
from DrissionPage import ChromiumPage
|
||||
from DrissionPage import ChromiumOptions
|
||||
|
||||
from bitmart.api_contract import APIContract
|
||||
|
||||
|
||||
class BitmartFuturesTransaction:
|
||||
def __init__(self, bit_id):
|
||||
|
||||
self.page: ChromiumPage | None = None
|
||||
|
||||
self.api_key = "a0fb7b98464fd9bcce67e7c519d58ec10d0c38a8"
|
||||
self.secret_key = "4eaeba78e77aeaab1c2027f846a276d164f264a44c2c1bb1c5f3be50c8de1ca5"
|
||||
self.memo = "合约交易"
|
||||
|
||||
self.contract_symbol = "ETHUSDT"
|
||||
|
||||
self.contractAPI = APIContract(self.api_key, self.secret_key, self.memo, timeout=(5, 15))
|
||||
|
||||
self.start = 0 # 持仓状态: -1 空, 0 无, 1 多
|
||||
|
||||
self.pbar = tqdm(total=30, desc="等待K线", ncols=80) # 可选:用于长时间等待时展示进度
|
||||
|
||||
self.last_kline_time = None # 上一次处理的K线时间戳,用于判断是否是新K线
|
||||
|
||||
# 反手频率控制
|
||||
self.reverse_cooldown_seconds = 1.5 * 60 # 反手冷却时间(秒)
|
||||
self.reverse_min_move_pct = 0.05 # 反手最小价差过滤(百分比)
|
||||
self.last_reverse_time = None # 上次反手时间
|
||||
|
||||
# 开仓频率控制
|
||||
self.open_cooldown_seconds = 60 # 开仓冷却时间(秒),两次开仓至少间隔此时长
|
||||
self.last_open_time = None # 上次开仓时间
|
||||
self.last_open_kline_id = None # 上次开仓所在 K 线 id,同一根 K 线只允许开仓一次
|
||||
|
||||
self.leverage = "100" # 高杠杆(全仓模式下可开更大仓位)
|
||||
self.open_type = "cross" # 全仓模式
|
||||
self.risk_percent = 0 # 未使用;若启用则可为每次开仓占可用余额的百分比
|
||||
self.take_profit_usd = 5 # 仓位盈利达到此金额(美元)时平仓止盈
|
||||
self.stop_loss_usd = -3 # 固定止损:亏损达到 3 美元平仓
|
||||
self.trailing_activation_usd = 2 # 盈利达到此金额后启动移动止损
|
||||
self.trailing_distance_usd = 1.5 # 从最高盈利回撤此金额则平仓
|
||||
self.max_unrealized_pnl_seen = None # 持仓期间见过的最大盈利(用于移动止损)
|
||||
|
||||
self.open_avg_price = None # 开仓价格
|
||||
self.current_amount = None # 持仓量
|
||||
|
||||
self.bit_id = bit_id
|
||||
self.default_order_size = 25 # 开仓/反手张数,统一在此修改
|
||||
|
||||
# 策略相关变量
|
||||
self.prev_kline = None # 上一根K线
|
||||
self.current_kline = None # 当前K线
|
||||
self.prev_entity = None # 上一根K线实体大小
|
||||
self.current_open = None # 当前K线开盘价
|
||||
|
||||
def get_klines(self):
|
||||
"""获取最近2根K线(当前K线和上一根K线)"""
|
||||
try:
|
||||
end_time = int(time.time())
|
||||
# 获取足够多的条目确保有最新的K线
|
||||
response = self.contractAPI.get_kline(
|
||||
contract_symbol=self.contract_symbol,
|
||||
step=5, # 5分钟
|
||||
start_time=end_time - 3600 * 3, # 取最近3小时
|
||||
end_time=end_time
|
||||
)[0]["data"]
|
||||
|
||||
# 每根: [timestamp, open, high, low, close, volume]
|
||||
formatted = []
|
||||
for k in response:
|
||||
formatted.append({
|
||||
'id': int(k["timestamp"]),
|
||||
'open': float(k["open_price"]),
|
||||
'high': float(k["high_price"]),
|
||||
'low': float(k["low_price"]),
|
||||
'close': float(k["close_price"])
|
||||
})
|
||||
formatted.sort(key=lambda x: x['id'])
|
||||
|
||||
# 返回最近2根K线:倒数第二根(上一根)和最后一根(当前)
|
||||
if len(formatted) >= 2:
|
||||
return formatted[-2], formatted[-1]
|
||||
return None, None
|
||||
except Exception as e:
|
||||
logger.error(f"获取K线异常: {e}")
|
||||
self.ding(text="获取K线异常", error=True)
|
||||
return None, None
|
||||
|
||||
def get_current_price(self):
|
||||
"""获取当前最新价格"""
|
||||
try:
|
||||
end_time = int(time.time())
|
||||
response = self.contractAPI.get_kline(
|
||||
contract_symbol=self.contract_symbol,
|
||||
step=1, # 1分钟
|
||||
start_time=end_time - 3600 * 1, # 取最近1小时
|
||||
end_time=end_time
|
||||
)[0]
|
||||
if response['code'] == 1000:
|
||||
return float(response['data'][-1]["close_price"])
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"获取价格异常: {e}")
|
||||
return None
|
||||
|
||||
def get_available_balance(self):
|
||||
"""获取合约账户可用USDT余额"""
|
||||
try:
|
||||
response = self.contractAPI.get_assets_detail()[0]
|
||||
if response['code'] == 1000:
|
||||
data = response['data']
|
||||
if isinstance(data, dict):
|
||||
return float(data.get('available_balance', 0))
|
||||
elif isinstance(data, list):
|
||||
for asset in data:
|
||||
if asset.get('currency') == 'USDT':
|
||||
return float(asset.get('available_balance', 0))
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"余额查询异常: {e}")
|
||||
return None
|
||||
|
||||
def get_position_status(self):
|
||||
"""获取当前持仓方向"""
|
||||
try:
|
||||
response = self.contractAPI.get_position(contract_symbol=self.contract_symbol)[0]
|
||||
if response['code'] == 1000:
|
||||
positions = response['data']
|
||||
if not positions:
|
||||
self.start = 0
|
||||
self.open_avg_price = None
|
||||
self.current_amount = None
|
||||
self.unrealized_pnl = None
|
||||
return True
|
||||
pos = positions[0]
|
||||
self.start = 1 if pos['position_type'] == 1 else -1
|
||||
self.open_avg_price = float(pos['open_avg_price'])
|
||||
self.current_amount = float(pos['current_amount'])
|
||||
self.position_cross = pos["position_cross"]
|
||||
# 直接从API获取未实现盈亏(Bitmart返回的是 unrealized_value 字段)
|
||||
self.unrealized_pnl = float(pos.get('unrealized_value', 0))
|
||||
logger.debug(f"持仓详情: 方向={self.start}, 开仓均价={self.open_avg_price}, "
|
||||
f"持仓量={self.current_amount}, 未实现盈亏={self.unrealized_pnl:.2f}")
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"持仓查询异常: {e}")
|
||||
return False
|
||||
|
||||
def get_unrealized_pnl_usd(self):
|
||||
"""
|
||||
获取当前持仓未实现盈亏(美元),直接使用API返回值
|
||||
"""
|
||||
if self.start == 0 or self.unrealized_pnl is None:
|
||||
return None
|
||||
return self.unrealized_pnl
|
||||
|
||||
def set_leverage(self):
|
||||
"""程序启动时设置全仓 + 高杠杆"""
|
||||
try:
|
||||
response = self.contractAPI.post_submit_leverage(
|
||||
contract_symbol=self.contract_symbol,
|
||||
leverage=self.leverage,
|
||||
open_type=self.open_type
|
||||
)[0]
|
||||
if response['code'] == 1000:
|
||||
logger.success(f"全仓模式 + {self.leverage}x 杠杆设置成功")
|
||||
return True
|
||||
else:
|
||||
logger.error(f"杠杆设置失败: {response}")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"设置杠杆异常: {e}")
|
||||
return False
|
||||
|
||||
def openBrowser(self):
|
||||
"""打开 TGE 对应浏览器实例"""
|
||||
try:
|
||||
bit_port = openBrowser(id=self.bit_id)
|
||||
co = ChromiumOptions()
|
||||
co.set_local_port(port=bit_port)
|
||||
self.page = ChromiumPage(addr_or_opts=co)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def click_safe(self, xpath, sleep=0.5):
|
||||
"""安全点击"""
|
||||
try:
|
||||
ele = self.page.ele(xpath)
|
||||
if not ele:
|
||||
return False
|
||||
# ele.scroll.to_see(center=True)
|
||||
# time.sleep(sleep)
|
||||
ele.click(by_js=True)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def 平仓(self):
|
||||
"""平仓操作"""
|
||||
self.click_safe('x://span[normalize-space(text()) ="市价"]')
|
||||
|
||||
def 开单(self, marketPriceLongOrder=0, limitPriceShortOrder=0, size=None, price=None):
|
||||
"""
|
||||
marketPriceLongOrder 市价做多或者做空,1是做多,-1是做空
|
||||
limitPriceShortOrder 限价做多或者做空
|
||||
"""
|
||||
if marketPriceLongOrder == -1:
|
||||
# self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
# self.page.ele('x://*[@id="size_0"]').input(vals=size, clear=True)
|
||||
self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]')
|
||||
elif marketPriceLongOrder == 1:
|
||||
# self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
# self.page.ele('x://*[@id="size_0"]').input(vals=size, clear=True)
|
||||
self.click_safe('x://span[normalize-space(text()) ="买入/做多"]')
|
||||
|
||||
if limitPriceShortOrder == -1:
|
||||
self.click_safe('x://button[normalize-space(text()) ="限价"]')
|
||||
self.page.ele('x://*[@id="price_0"]').input(vals=price, clear=True)
|
||||
time.sleep(1)
|
||||
self.page.ele('x://*[@id="size_0"]').input(1)
|
||||
self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]')
|
||||
elif limitPriceShortOrder == 1:
|
||||
self.click_safe('x://button[normalize-space(text()) ="限价"]')
|
||||
self.page.ele('x://*[@id="price_0"]').input(vals=price, clear=True)
|
||||
time.sleep(1)
|
||||
self.page.ele('x://*[@id="size_0"]').input(1)
|
||||
self.click_safe('x://span[normalize-space(text()) ="买入/做多"]')
|
||||
|
||||
def ding(self, text, error=False):
|
||||
"""日志通知"""
|
||||
if error:
|
||||
logger.error(text)
|
||||
else:
|
||||
logger.info(text)
|
||||
|
||||
def calculate_entity(self, kline):
|
||||
"""计算K线实体大小(绝对值)"""
|
||||
return abs(kline['close'] - kline['open'])
|
||||
|
||||
def calculate_upper_shadow(self, kline):
|
||||
"""计算上阴线(上影线)涨幅百分比"""
|
||||
# 上阴线 = (最高价 - max(开盘价, 收盘价)) / max(开盘价, 收盘价)
|
||||
body_top = max(kline['open'], kline['close'])
|
||||
if body_top == 0:
|
||||
return 0
|
||||
return (kline['high'] - body_top) / body_top * 100
|
||||
|
||||
def calculate_lower_shadow(self, kline):
|
||||
"""计算下阴线(下影线)跌幅百分比"""
|
||||
# 下阴线 = (min(开盘价, 收盘价) - 最低价) / min(开盘价, 收盘价)
|
||||
body_bottom = min(kline['open'], kline['close'])
|
||||
if body_bottom == 0:
|
||||
return 0
|
||||
return (body_bottom - kline['low']) / body_bottom * 100
|
||||
|
||||
def get_entity_edge(self, kline):
|
||||
"""获取K线实体边(收盘价或开盘价,取决于是阳线还是阴线)"""
|
||||
# 阳线(收盘>开盘):实体上边=收盘价,实体下边=开盘价
|
||||
# 阴线(收盘<开盘):实体上边=开盘价,实体下边=收盘价
|
||||
return {
|
||||
'upper': max(kline['open'], kline['close']), # 实体上边
|
||||
'lower': min(kline['open'], kline['close']) # 实体下边
|
||||
}
|
||||
|
||||
def check_signal(self, current_price, prev_kline, current_kline):
|
||||
"""
|
||||
检查交易信号
|
||||
返回: ('long', trigger_price) / ('short', trigger_price) / None
|
||||
"""
|
||||
# 计算上一根K线实体
|
||||
prev_entity = self.calculate_entity(prev_kline)
|
||||
|
||||
# 实体过小不交易(实体 < 0.1)
|
||||
if prev_entity < 0.1:
|
||||
logger.info(f"上一根K线实体过小: {prev_entity:.4f},跳过信号检测")
|
||||
return None
|
||||
|
||||
# 获取上一根K线的实体上下边
|
||||
prev_entity_edge = self.get_entity_edge(prev_kline)
|
||||
prev_entity_upper = prev_entity_edge['upper'] # 实体上边
|
||||
prev_entity_lower = prev_entity_edge['lower'] # 实体下边
|
||||
|
||||
# 优化:以下两种情况以当前这根的开盘价作为计算基准
|
||||
# 1) 上一根阳线 且 当前开盘价 > 上一根收盘价(跳空高开)
|
||||
# 2) 上一根阴线 且 当前开盘价 < 上一根收盘价(跳空低开)
|
||||
prev_is_bullish_for_calc = prev_kline['close'] > prev_kline['open']
|
||||
prev_is_bearish_for_calc = prev_kline['close'] < prev_kline['open']
|
||||
current_open_above_prev_close = current_kline['open'] > prev_kline['close']
|
||||
current_open_below_prev_close = current_kline['open'] < prev_kline['close']
|
||||
use_current_open_as_base = (prev_is_bullish_for_calc and current_open_above_prev_close) or (prev_is_bearish_for_calc and current_open_below_prev_close)
|
||||
|
||||
if use_current_open_as_base:
|
||||
# 以当前K线开盘价为基准计算(跳空时用当前开盘价参与计算)
|
||||
calc_lower = current_kline['open']
|
||||
calc_upper = current_kline['open'] # 同一基准,上下四分之一对称
|
||||
long_trigger = calc_lower + prev_entity / 4
|
||||
short_trigger = calc_upper - prev_entity / 4
|
||||
long_breakout = calc_upper + prev_entity / 4
|
||||
short_breakout = calc_lower - prev_entity / 4
|
||||
else:
|
||||
# 原有计算方式
|
||||
long_trigger = prev_entity_lower + prev_entity / 4 # 做多触发价 = 实体下边 + 实体/4(下四分之一处)
|
||||
short_trigger = prev_entity_upper - prev_entity / 4 # 做空触发价 = 实体上边 - 实体/4(上四分之一处)
|
||||
long_breakout = prev_entity_upper + prev_entity / 4 # 做多突破价 = 实体上边 + 实体/4
|
||||
short_breakout = prev_entity_lower - prev_entity / 4 # 做空突破价 = 实体下边 - 实体/4
|
||||
|
||||
# 上一根阴线 + 当前阳线:做多形态,不按上一根K线上三分之一做空
|
||||
prev_is_bearish = prev_kline['close'] < prev_kline['open']
|
||||
current_is_bullish = current_kline['close'] > current_kline['open']
|
||||
skip_short_by_upper_third = prev_is_bearish and current_is_bullish
|
||||
# 上一根阳线 + 当前阴线:做空形态,不按上一根K线下三分之一做多
|
||||
prev_is_bullish = prev_kline['close'] > prev_kline['open']
|
||||
current_is_bearish = current_kline['close'] < current_kline['open']
|
||||
skip_long_by_lower_third = prev_is_bullish and current_is_bearish
|
||||
|
||||
if use_current_open_as_base:
|
||||
if prev_is_bullish_for_calc and current_open_above_prev_close:
|
||||
logger.info(f"上一根阳线且当前开盘价({current_kline['open']:.2f})>上一根收盘价({prev_kline['close']:.2f}),以当前开盘价为基准计算")
|
||||
else:
|
||||
logger.info(f"上一根阴线且当前开盘价({current_kline['open']:.2f})<上一根收盘价({prev_kline['close']:.2f}),以当前开盘价为基准计算")
|
||||
logger.info(f"当前价格: {current_price:.2f}, 上一根实体: {prev_entity:.4f}")
|
||||
logger.info(f"上一根实体上边: {prev_entity_upper:.2f}, 下边: {prev_entity_lower:.2f}")
|
||||
logger.info(f"做多触发价(下1/4): {long_trigger:.2f}, 做空触发价(上1/4): {short_trigger:.2f}")
|
||||
logger.info(f"突破做多价(上1/4外): {long_breakout:.2f}, 突破做空价(下1/4外): {short_breakout:.2f}")
|
||||
if skip_short_by_upper_third:
|
||||
logger.info("上一根阴线+当前阳线(做多形态),不按上四分之一做空")
|
||||
if skip_long_by_lower_third:
|
||||
logger.info("上一根阳线+当前阴线(做空形态),不按下四分之一做多")
|
||||
|
||||
# 无持仓时检查开仓信号
|
||||
if self.start == 0:
|
||||
if current_price >= long_breakout and not skip_long_by_lower_third:
|
||||
logger.info(f"触发做多信号!价格 {current_price:.2f} >= 突破价(上1/4外) {long_breakout:.2f}")
|
||||
return ('long', long_breakout)
|
||||
elif current_price <= short_breakout and not skip_short_by_upper_third:
|
||||
logger.info(f"触发做空信号!价格 {current_price:.2f} <= 突破价(下1/4外) {short_breakout:.2f}")
|
||||
return ('short', short_breakout)
|
||||
|
||||
# 持仓时检查反手信号
|
||||
elif self.start == 1: # 持多仓
|
||||
# 反手条件1: 价格跌到上一根K线的上三分之一处(做空触发价);上一根阴线+当前阳线做多时跳过
|
||||
if current_price <= short_trigger and not skip_short_by_upper_third:
|
||||
logger.info(f"持多反手做空!价格 {current_price:.2f} <= 触发价(上1/4) {short_trigger:.2f}")
|
||||
return ('reverse_short', short_trigger)
|
||||
|
||||
# 反手条件2: 上一根K线上阴线涨幅>0.01%,当前跌到上一根实体下边
|
||||
upper_shadow_pct = self.calculate_upper_shadow(prev_kline)
|
||||
if upper_shadow_pct > 0.01 and current_price <= prev_entity_lower:
|
||||
logger.info(f"持多反手做空!上阴线涨幅 {upper_shadow_pct:.4f}% > 0.01%,"
|
||||
f"价格 {current_price:.2f} <= 实体下边 {prev_entity_lower:.2f}")
|
||||
return ('reverse_short', prev_entity_lower)
|
||||
|
||||
elif self.start == -1: # 持空仓
|
||||
# 反手条件1: 价格涨到上一根K线的下三分之一处(做多触发价);上一根阳线+当前阴线做空时跳过
|
||||
if current_price >= long_trigger and not skip_long_by_lower_third:
|
||||
logger.info(f"持空反手做多!价格 {current_price:.2f} >= 触发价(下1/4) {long_trigger:.2f}")
|
||||
return ('reverse_long', long_trigger)
|
||||
|
||||
# 反手条件2: 上一根K线下阴线跌幅>0.01%,当前涨到上一根实体上边
|
||||
lower_shadow_pct = self.calculate_lower_shadow(prev_kline)
|
||||
if lower_shadow_pct > 0.01 and current_price >= prev_entity_upper:
|
||||
logger.info(f"持空反手做多!下阴线跌幅 {lower_shadow_pct:.4f}% > 0.01%,"
|
||||
f"价格 {current_price:.2f} >= 实体上边 {prev_entity_upper:.2f}")
|
||||
return ('reverse_long', prev_entity_upper)
|
||||
|
||||
return None
|
||||
|
||||
def can_open(self, current_kline_id):
|
||||
"""开仓前过滤:同一根 K 线只开一次 + 开仓冷却时间。仅用于 long/short 新开仓。"""
|
||||
now = time.time()
|
||||
if self.last_open_kline_id is not None and self.last_open_kline_id == current_kline_id:
|
||||
logger.info(f"开仓频率控制:本 K 线({current_kline_id})已开过仓,跳过")
|
||||
return False
|
||||
if self.last_open_time is not None and now - self.last_open_time < self.open_cooldown_seconds:
|
||||
remain = self.open_cooldown_seconds - (now - self.last_open_time)
|
||||
logger.info(f"开仓冷却中,剩余 {remain:.0f} 秒")
|
||||
return False
|
||||
return True
|
||||
|
||||
def can_reverse(self, current_price, trigger_price):
|
||||
"""反手前过滤:冷却时间 + 最小价差"""
|
||||
now = time.time()
|
||||
if self.last_reverse_time and now - self.last_reverse_time < self.reverse_cooldown_seconds:
|
||||
remain = self.reverse_cooldown_seconds - (now - self.last_reverse_time)
|
||||
logger.info(f"反手冷却中,剩余 {remain:.0f} 秒")
|
||||
return False
|
||||
|
||||
if trigger_price and trigger_price > 0:
|
||||
move_pct = abs(current_price - trigger_price) / trigger_price * 100
|
||||
if move_pct < self.reverse_min_move_pct:
|
||||
logger.info(f"反手价差不足: {move_pct:.4f}% < {self.reverse_min_move_pct}%")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def verify_no_position(self, max_retries=5, retry_interval=3):
|
||||
"""
|
||||
验证当前无持仓
|
||||
返回: True 表示无持仓可以开仓,False 表示有持仓不能开仓
|
||||
"""
|
||||
for i in range(max_retries):
|
||||
if self.get_position_status():
|
||||
if self.start == 0:
|
||||
logger.info(f"确认无持仓,可以开仓")
|
||||
return True
|
||||
else:
|
||||
logger.warning(
|
||||
f"仍有持仓 (方向: {self.start}),等待 {retry_interval} 秒后重试 ({i + 1}/{max_retries})")
|
||||
time.sleep(retry_interval)
|
||||
else:
|
||||
logger.warning(f"查询持仓状态失败,等待 {retry_interval} 秒后重试 ({i + 1}/{max_retries})")
|
||||
time.sleep(retry_interval)
|
||||
|
||||
logger.error(f"经过 {max_retries} 次重试仍有持仓或查询失败,放弃开仓")
|
||||
return False
|
||||
|
||||
def verify_position_direction(self, expected_direction):
|
||||
"""
|
||||
验证当前持仓方向是否与预期一致
|
||||
expected_direction: 1 多仓, -1 空仓
|
||||
返回: True 表示持仓方向正确,False 表示不正确
|
||||
"""
|
||||
if self.get_position_status():
|
||||
if self.start == expected_direction:
|
||||
logger.info(f"持仓方向验证成功: {self.start}")
|
||||
return True
|
||||
else:
|
||||
logger.warning(f"持仓方向不符: 期望 {expected_direction}, 实际 {self.start}")
|
||||
return False
|
||||
else:
|
||||
logger.error("查询持仓状态失败")
|
||||
return False
|
||||
|
||||
def execute_trade(self, signal, size=None):
|
||||
"""执行交易。size 不传或为 None 时使用 default_order_size。"""
|
||||
signal_type, trigger_price = signal
|
||||
size = self.default_order_size if size is None else size
|
||||
|
||||
if signal_type == 'long':
|
||||
# 开多前先确认无持仓
|
||||
logger.info(f"准备开多,触发价: {trigger_price:.2f}")
|
||||
if not self.get_position_status():
|
||||
logger.error("开仓前查询持仓状态失败,放弃开仓")
|
||||
return False
|
||||
if self.start != 0:
|
||||
logger.warning(f"开多前发现已有持仓 (方向: {self.start}),放弃开仓避免双向持仓")
|
||||
return False
|
||||
|
||||
logger.info(f"确认无持仓,执行开多")
|
||||
self.开单(marketPriceLongOrder=1, size=size)
|
||||
time.sleep(3) # 等待订单执行
|
||||
|
||||
# 验证开仓是否成功
|
||||
if self.verify_position_direction(1):
|
||||
self.max_unrealized_pnl_seen = None # 新仓位重置移动止损记录
|
||||
self.last_open_time = time.time()
|
||||
self.last_open_kline_id = getattr(self, "_current_kline_id_for_open", None)
|
||||
logger.success("开多成功")
|
||||
return True
|
||||
else:
|
||||
logger.error("开多后持仓验证失败")
|
||||
return False
|
||||
|
||||
elif signal_type == 'short':
|
||||
# 开空前先确认无持仓
|
||||
logger.info(f"准备开空,触发价: {trigger_price:.2f}")
|
||||
if not self.get_position_status():
|
||||
logger.error("开仓前查询持仓状态失败,放弃开仓")
|
||||
return False
|
||||
if self.start != 0:
|
||||
logger.warning(f"开空前发现已有持仓 (方向: {self.start}),放弃开仓避免双向持仓")
|
||||
return False
|
||||
|
||||
logger.info(f"确认无持仓,执行开空")
|
||||
self.开单(marketPriceLongOrder=-1, size=size)
|
||||
time.sleep(3) # 等待订单执行
|
||||
|
||||
# 验证开仓是否成功
|
||||
if self.verify_position_direction(-1):
|
||||
self.max_unrealized_pnl_seen = None # 新仓位重置移动止损记录
|
||||
self.last_open_time = time.time()
|
||||
self.last_open_kline_id = getattr(self, "_current_kline_id_for_open", None)
|
||||
logger.success("开空成功")
|
||||
return True
|
||||
else:
|
||||
logger.error("开空后持仓验证失败")
|
||||
return False
|
||||
|
||||
elif signal_type == 'reverse_long':
|
||||
# 平空 + 开多(反手做多):先平仓,确认无仓后再开多,避免双向持仓
|
||||
logger.info(f"执行反手做多,触发价: {trigger_price:.2f}")
|
||||
self.平仓()
|
||||
time.sleep(1) # 给交易所处理平仓的时间
|
||||
# 轮询确认已无持仓再开多(最多等约 10 秒)
|
||||
for _ in range(10):
|
||||
if self.get_position_status() and self.start == 0:
|
||||
break
|
||||
time.sleep(1)
|
||||
if self.start != 0:
|
||||
logger.warning("反手做多:平仓后仍有持仓,放弃本次开多")
|
||||
return False
|
||||
logger.info("已确认无持仓,执行开多")
|
||||
self.开单(marketPriceLongOrder=1, size=size)
|
||||
time.sleep(3)
|
||||
|
||||
if self.verify_position_direction(1):
|
||||
self.max_unrealized_pnl_seen = None
|
||||
logger.success("反手做多成功")
|
||||
self.last_reverse_time = time.time()
|
||||
time.sleep(20)
|
||||
return True
|
||||
else:
|
||||
logger.error("反手做多后持仓验证失败")
|
||||
return False
|
||||
|
||||
elif signal_type == 'reverse_short':
|
||||
# 平多 + 开空(反手做空):先平仓,确认无仓后再开空
|
||||
logger.info(f"执行反手做空,触发价: {trigger_price:.2f}")
|
||||
self.平仓()
|
||||
time.sleep(1)
|
||||
for _ in range(10):
|
||||
if self.get_position_status() and self.start == 0:
|
||||
break
|
||||
time.sleep(1)
|
||||
if self.start != 0:
|
||||
logger.warning("反手做空:平仓后仍有持仓,放弃本次开空")
|
||||
return False
|
||||
logger.info("已确认无持仓,执行开空")
|
||||
self.开单(marketPriceLongOrder=-1, size=size)
|
||||
time.sleep(3)
|
||||
|
||||
if self.verify_position_direction(-1):
|
||||
self.max_unrealized_pnl_seen = None
|
||||
logger.success("反手做空成功")
|
||||
self.last_reverse_time = time.time()
|
||||
time.sleep(20)
|
||||
return True
|
||||
else:
|
||||
logger.error("反手做空后持仓验证失败")
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
def action(self):
|
||||
"""主循环"""
|
||||
|
||||
logger.info("开始运行四分之一策略交易...")
|
||||
|
||||
# 启动时设置全仓高杠杆
|
||||
if not self.set_leverage():
|
||||
logger.error("杠杆设置失败,程序继续运行但可能下单失败")
|
||||
return
|
||||
|
||||
page_start = True
|
||||
|
||||
while True:
|
||||
|
||||
if page_start:
|
||||
# 打开浏览器
|
||||
for i in range(5):
|
||||
if self.openBrowser():
|
||||
logger.info("浏览器打开成功")
|
||||
break
|
||||
else:
|
||||
self.ding("打开浏览器失败!", error=True)
|
||||
return
|
||||
|
||||
# 进入交易页面
|
||||
self.page.get("https://derivatives.bitmart.com/zh-CN/futures/ETHUSDT")
|
||||
self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
|
||||
self.page.ele('x://*[@id="size_0"]').input(vals=25, clear=True)
|
||||
|
||||
page_start = False
|
||||
|
||||
try:
|
||||
# 1. 获取K线数据(当前K线和上一根K线)
|
||||
prev_kline, current_kline = self.get_klines()
|
||||
if not prev_kline or not current_kline:
|
||||
logger.warning("获取K线失败,等待重试...")
|
||||
time.sleep(5)
|
||||
continue
|
||||
|
||||
# 记录进入新的K线
|
||||
current_kline_time = current_kline['id']
|
||||
if self.last_kline_time != current_kline_time:
|
||||
self.last_kline_time = current_kline_time
|
||||
logger.info(f"进入新K线: {current_kline_time}")
|
||||
|
||||
# 2. 获取当前价格
|
||||
current_price = self.get_current_price()
|
||||
if not current_price:
|
||||
logger.warning("获取价格失败,等待重试...")
|
||||
time.sleep(2)
|
||||
continue
|
||||
|
||||
# 3. 每次循环都通过SDK获取真实持仓状态(避免状态不同步导致双向持仓)
|
||||
if not self.get_position_status():
|
||||
logger.warning("获取持仓状态失败,等待重试...")
|
||||
time.sleep(2)
|
||||
continue
|
||||
|
||||
logger.debug(f"当前持仓状态: {self.start} (0=无, 1=多, -1=空)")
|
||||
|
||||
# 3.5 止损/止盈/移动止损
|
||||
if self.start != 0:
|
||||
pnl_usd = self.get_unrealized_pnl_usd()
|
||||
if pnl_usd is not None:
|
||||
# 固定止损:亏损达到 3 美元平仓
|
||||
if pnl_usd <= self.stop_loss_usd:
|
||||
logger.info(f"仓位亏损 {pnl_usd:.2f} 美元 <= 止损 {self.stop_loss_usd} 美元,执行止损平仓")
|
||||
self.平仓()
|
||||
self.max_unrealized_pnl_seen = None
|
||||
time.sleep(3)
|
||||
continue
|
||||
# 更新持仓期间最大盈利(用于移动止损)
|
||||
if self.max_unrealized_pnl_seen is None:
|
||||
self.max_unrealized_pnl_seen = pnl_usd
|
||||
else:
|
||||
self.max_unrealized_pnl_seen = max(self.max_unrealized_pnl_seen, pnl_usd)
|
||||
# 移动止损:盈利曾达到 activation 后,从最高盈利回撤 trailing_distance 则平仓
|
||||
if self.max_unrealized_pnl_seen >= self.trailing_activation_usd:
|
||||
if pnl_usd < self.max_unrealized_pnl_seen - self.trailing_distance_usd:
|
||||
logger.info(f"移动止损:当前盈利 {pnl_usd:.2f} 从最高 {self.max_unrealized_pnl_seen:.2f} 回撤 >= {self.trailing_distance_usd} 美元,平仓")
|
||||
self.平仓()
|
||||
self.max_unrealized_pnl_seen = None
|
||||
time.sleep(3)
|
||||
continue
|
||||
# 止盈:盈利达到 take_profit_usd 平仓
|
||||
if pnl_usd >= self.take_profit_usd:
|
||||
logger.info(f"仓位盈利 {pnl_usd:.2f} 美元 >= {self.take_profit_usd} 美元,执行止盈平仓")
|
||||
self.平仓()
|
||||
self.max_unrealized_pnl_seen = None
|
||||
time.sleep(3)
|
||||
continue
|
||||
|
||||
# 4. 检查信号
|
||||
signal = self.check_signal(current_price, prev_kline, current_kline)
|
||||
|
||||
# 5. 反手过滤:冷却时间 + 最小价差
|
||||
if signal and signal[0].startswith('reverse_'):
|
||||
if not self.can_reverse(current_price, signal[1]):
|
||||
signal = None
|
||||
|
||||
# 5.5 开仓频率过滤:同一根 K 线只开一次 + 开仓冷却
|
||||
if signal and signal[0] in ('long', 'short'):
|
||||
if not self.can_open(current_kline_time):
|
||||
signal = None
|
||||
else:
|
||||
self._current_kline_id_for_open = current_kline_time # 供 execute_trade 成功后记录
|
||||
|
||||
# 6. 有信号则执行交易
|
||||
if signal:
|
||||
trade_success = self.execute_trade(signal)
|
||||
if trade_success:
|
||||
logger.success(f"交易执行完成: {signal[0]}, 当前持仓状态: {self.start}")
|
||||
page_start = True
|
||||
else:
|
||||
logger.warning(f"交易执行失败或被阻止: {signal[0]}")
|
||||
|
||||
# 短暂等待后继续循环(同一根K线遇到信号就操作)
|
||||
time.sleep(0.1)
|
||||
|
||||
if page_start:
|
||||
self.page.close()
|
||||
time.sleep(5)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logger.info("用户中断,程序退出")
|
||||
break
|
||||
except Exception as e:
|
||||
logger.error(f"主循环异常: {e}")
|
||||
time.sleep(5)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
BitmartFuturesTransaction(bit_id="f2320f57e24c45529a009e1541e25961").action()
|
||||
992
bitmart/四分之一,五分钟,反手条件充足修改版.py
Normal file
992
bitmart/四分之一,五分钟,反手条件充足修改版.py
Normal file
@@ -0,0 +1,992 @@
|
||||
import sys
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
from tqdm import tqdm
|
||||
from loguru import logger
|
||||
from bit_tools import openBrowser
|
||||
from DrissionPage import ChromiumPage
|
||||
from DrissionPage import ChromiumOptions
|
||||
from bitmart.api_contract import APIContract
|
||||
|
||||
from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
from rich.table import Table
|
||||
from rich.layout import Layout
|
||||
from rich.align import Align
|
||||
from rich.text import Text
|
||||
from rich.live import Live
|
||||
|
||||
# 是否使用 Rich 仪表盘(否则用 loguru 普通输出)
|
||||
USE_RICH_DASHBOARD = True
|
||||
console = Console()
|
||||
|
||||
# 颜色标签(loguru 用,无 Rich 时)
|
||||
_R = "\033[0m"
|
||||
_B = "\033[1m"
|
||||
_C = "\033[36m"
|
||||
_Y = "\033[33m"
|
||||
_G = "\033[32m"
|
||||
_M = "\033[90m"
|
||||
_W = "\033[97m"
|
||||
|
||||
def _tag(t: str, color: str) -> str:
|
||||
return color + "[" + t + "]" + _R + " "
|
||||
|
||||
LOG_PRICE = _tag("价格", _C)
|
||||
LOG_SIGNAL = _tag("信号", _Y)
|
||||
LOG_POSITION = _tag("仓位", _G)
|
||||
LOG_SYSTEM = _tag("系统", _M)
|
||||
|
||||
if not USE_RICH_DASHBOARD:
|
||||
logger.remove()
|
||||
logger.add(sys.stderr, format="\033[2m│\033[0m {message}", colorize=False)
|
||||
|
||||
def log_kline_header(kline_id):
|
||||
"""新 K 线分块头(仅非 Rich 模式使用)"""
|
||||
s = f" K 线 {kline_id} "
|
||||
width = max(52, len(s) + 4)
|
||||
line = "─" * (width - 2)
|
||||
pad_left = (width - 2 - len(s)) // 2
|
||||
pad_right = width - 2 - len(s) - pad_left
|
||||
logger.info(_M + "╭" + line + "╮" + _R)
|
||||
logger.info(_M + "│" + _R + " " * pad_left + _B + _W + s + _R + " " * pad_right + _M + "│" + _R)
|
||||
logger.info(_M + "╰" + line + "╯" + _R)
|
||||
|
||||
|
||||
# ---------- Rich 仪表盘 ----------
|
||||
def make_header() -> Panel:
|
||||
title = Text("四分之一策略 · ETHUSDT 5m", style="bold cyan")
|
||||
subtitle = Text(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), style="dim")
|
||||
return Panel(Align.center(title + Text("\n") + subtitle), border_style="cyan")
|
||||
|
||||
def make_metrics_panel(state: dict) -> Panel:
|
||||
t = Table(show_header=True, header_style="bold magenta", expand=True)
|
||||
t.add_column("指标", style="cyan")
|
||||
t.add_column("数值", justify="right", style="green")
|
||||
t.add_row("当前价", f"{state.get('price', 0):.2f}")
|
||||
pos_map = {0: "无", 1: "多", -1: "空"}
|
||||
t.add_row("持仓", pos_map.get(state.get("position", 0), "?"))
|
||||
t.add_row("K线 ID", str(state.get("kline_id", "-")))
|
||||
t.add_row("做多触发", f"{state.get('long_trigger', 0):.2f}")
|
||||
t.add_row("做空触发", f"{state.get('short_trigger', 0):.2f}")
|
||||
t.add_row("突破做多", f"{state.get('long_breakout', 0):.2f}")
|
||||
t.add_row("突破做空", f"{state.get('short_breakout', 0):.2f}")
|
||||
ema10 = state.get("ema10")
|
||||
atr14 = state.get("atr14")
|
||||
t.add_row("EMA10", f"{ema10:.2f}" if ema10 is not None else "-")
|
||||
t.add_row("ATR14", f"{atr14:.2f}" if atr14 is not None else "-")
|
||||
pnl = state.get("unrealized_pnl")
|
||||
t.add_row("未实现盈亏", f"{pnl:.2f}$" if pnl is not None else "-")
|
||||
return Panel(t, title="[bold]价格 / 指标[/bold]", border_style="magenta")
|
||||
|
||||
def make_logs_panel(logs: list) -> Panel:
|
||||
body = "\n".join(logs[-12:]) if logs else "等待日志..."
|
||||
text = Text.from_ansi(body) if body else Text("等待日志...", style="dim")
|
||||
return Panel(text, title="[bold]状态 / 日志[/bold]", border_style="green")
|
||||
|
||||
def make_footer(msg: str = "Ctrl+C 退出") -> Panel:
|
||||
return Panel(Align.center(Text(msg, style="bold yellow")), border_style="yellow")
|
||||
|
||||
def build_dashboard_layout(state: dict, logs: list) -> Layout:
|
||||
layout = Layout()
|
||||
layout.split_column(
|
||||
Layout(make_header(), name="header", size=5),
|
||||
Layout(name="body", ratio=1),
|
||||
Layout(make_footer(), name="footer", size=3),
|
||||
)
|
||||
layout["body"].split_row(
|
||||
Layout(make_metrics_panel(state), name="left", ratio=1),
|
||||
Layout(make_logs_panel(logs), name="right", ratio=2),
|
||||
)
|
||||
return layout
|
||||
|
||||
|
||||
class BitmartFuturesTransaction:
|
||||
def __init__(self, bit_id):
|
||||
|
||||
self.page: ChromiumPage | None = None
|
||||
|
||||
self.api_key = "a0fb7b98464fd9bcce67e7c519d58ec10d0c38a8"
|
||||
self.secret_key = "4eaeba78e77aeaab1c2027f846a276d164f264a44c2c1bb1c5f3be50c8de1ca5"
|
||||
self.memo = "合约交易"
|
||||
|
||||
self.contract_symbol = "ETHUSDT"
|
||||
|
||||
self.contractAPI = APIContract(self.api_key, self.secret_key, self.memo, timeout=(5, 15))
|
||||
|
||||
self.start = 0 # 持仓状态: -1 空, 0 无, 1 多
|
||||
|
||||
self.pbar = tqdm(total=30, desc="等待K线", ncols=80) # 可选:用于长时间等待时展示进度
|
||||
|
||||
self.last_kline_time = None # 上一次处理的K线时间戳,用于判断是否是新K线
|
||||
|
||||
# 反手频率控制
|
||||
self.reverse_cooldown_seconds = 1.5 * 60 # 反手冷却时间(秒)
|
||||
self.reverse_min_move_pct = 0.05 # 反手最小价差过滤(百分比)
|
||||
self.last_reverse_time = None # 上次反手时间
|
||||
|
||||
# 开仓频率控制
|
||||
self.open_cooldown_seconds = 60 # 开仓冷却时间(秒),两次开仓至少间隔此时长
|
||||
self.last_open_time = None # 上次开仓时间
|
||||
self.last_open_kline_id = None # 上次开仓所在 K 线 id(仅记录,不限制单 K 线开仓次数)
|
||||
|
||||
self.leverage = "100" # 高杠杆(全仓模式下可开更大仓位)
|
||||
self.open_type = "cross" # 全仓模式
|
||||
self.risk_percent = 0 # 未使用;若启用则可为每次开仓占可用余额的百分比
|
||||
self.stop_loss_usd = -3 # 固定止损:亏损达到 3 美元平仓
|
||||
self.trailing_activation_usd = 2 # 盈利达到此金额后启动移动止损
|
||||
self.trailing_distance_usd = 1.5 # 从最高盈利回撤此金额则平仓
|
||||
self.max_unrealized_pnl_seen = None # 持仓期间见过的最大盈利(用于移动止损)
|
||||
# 当前K线从极值回落平仓
|
||||
# 模式: 'fixed'=固定点数(drop_from_high_to_close);'pct_retrace'=按本K线涨幅比例动态算回撤
|
||||
self.drop_from_high_mode = 'pct_retrace' # 'fixed' | 'pct_retrace'
|
||||
self.drop_from_high_to_close = 2 # fixed 模式下:回落/反弹超过此价格(点数)则平仓,0 表示关闭
|
||||
# pct_retrace 模式:本K线涨幅 = (最高-开盘)/开盘*100;允许回撤% = 涨幅% * retrace_ratio,从最高点回撤超过则平仓
|
||||
self.retrace_ratio = 0.5 # 回撤系数,如 0.5 表示允许回撤涨幅的 50%(类似斐波那契 50% 回撤)
|
||||
self.min_rise_pct_to_activate = 0.02 # 至少涨/跌这么多才启用动态回撤,避免噪音
|
||||
self.min_drop_pct_from_high = 0.03 # 至少从最高点回撤这么多%才平仓(保底,避免过于敏感)
|
||||
# EMA(10) + ATR(14) 平仓(多/空一致):多单跌破 EMA10 或从最高回撤≥ATR 平;空单涨破 EMA10 或从最低反弹≥ATR 平
|
||||
self.use_ema_atr_exit = True # 是否启用 EMA/ATR 平仓规则(多单+空单)
|
||||
self.atr_multiplier = 1.1 # 追踪止盈:从极值回撤/反弹 ≥ 此倍数 × ATR(14) 则平仓
|
||||
self._candle_high_seen = None # 当前K线内见过的最高价(多头用)
|
||||
self._candle_low_seen = None # 当前K线内见过的最低价(空头用)
|
||||
self._candle_id_for_high_low = None # 记录高低对应的K线 id,换线则重置
|
||||
|
||||
self.open_avg_price = None # 开仓价格
|
||||
self.current_amount = None # 持仓量
|
||||
|
||||
self.bit_id = bit_id
|
||||
self.default_order_size = 25 # 开仓/反手张数,统一在此修改
|
||||
|
||||
# 策略相关变量
|
||||
self.prev_kline = None
|
||||
self.current_kline = None
|
||||
self.prev_entity = None
|
||||
self.current_open = None
|
||||
# Rich 仪表盘:状态与日志(供 build_dashboard_layout 使用)
|
||||
self._display_state = {}
|
||||
self._display_logs = []
|
||||
self._display_triggers = {}
|
||||
|
||||
def get_klines(self):
|
||||
"""获取最近2根K线(当前K线和上一根K线)"""
|
||||
try:
|
||||
end_time = int(time.time())
|
||||
# 获取足够多的条目确保有最新的K线
|
||||
response = self.contractAPI.get_kline(
|
||||
contract_symbol=self.contract_symbol,
|
||||
step=5, # 5分钟
|
||||
start_time=end_time - 3600 * 3, # 取最近3小时
|
||||
end_time=end_time
|
||||
)[0]["data"]
|
||||
|
||||
# 每根: [timestamp, open, high, low, close, volume]
|
||||
formatted = []
|
||||
for k in response:
|
||||
formatted.append({
|
||||
'id': int(k["timestamp"]),
|
||||
'open': float(k["open_price"]),
|
||||
'high': float(k["high_price"]),
|
||||
'low': float(k["low_price"]),
|
||||
'close': float(k["close_price"])
|
||||
})
|
||||
formatted.sort(key=lambda x: x['id'])
|
||||
|
||||
# 返回最近2根K线:倒数第二根(上一根)和最后一根(当前)
|
||||
if len(formatted) >= 2:
|
||||
return formatted[-2], formatted[-1]
|
||||
return None, None
|
||||
except Exception as e:
|
||||
logger.error(f"获取K线异常: {e}")
|
||||
self.ding(text="获取K线异常", error=True)
|
||||
return None, None
|
||||
|
||||
def get_klines_series(self, count=35):
|
||||
"""获取最近 count 根 5 分钟 K 线(用于 EMA/ATR),按时间正序。最后一根为当前未收盘 K 线。"""
|
||||
try:
|
||||
end_time = int(time.time())
|
||||
response = self.contractAPI.get_kline(
|
||||
contract_symbol=self.contract_symbol,
|
||||
step=5,
|
||||
start_time=end_time - 3600 * 4,
|
||||
end_time=end_time
|
||||
)[0]["data"]
|
||||
formatted = []
|
||||
for k in response:
|
||||
formatted.append({
|
||||
'id': int(k["timestamp"]),
|
||||
'open': float(k["open_price"]),
|
||||
'high': float(k["high_price"]),
|
||||
'low': float(k["low_price"]),
|
||||
'close': float(k["close_price"])
|
||||
})
|
||||
formatted.sort(key=lambda x: x['id'])
|
||||
return formatted[-count:] if len(formatted) >= count else formatted
|
||||
except Exception as e:
|
||||
logger.error(f"获取K线序列异常: {e}")
|
||||
return []
|
||||
|
||||
@staticmethod
|
||||
def _ema(series, period):
|
||||
"""对序列 series(从旧到新)计算 EMA(period),返回最后一个 EMA 值。"""
|
||||
if not series or len(series) < period:
|
||||
return None
|
||||
alpha = 2.0 / (period + 1)
|
||||
ema = sum(series[:period]) / period # 用前 period 根收盘的 SMA 做种子
|
||||
for i in range(period, len(series)):
|
||||
ema = alpha * series[i] + (1 - alpha) * ema
|
||||
return ema
|
||||
|
||||
@staticmethod
|
||||
def _atr(klines, period=14):
|
||||
"""对 klines(从旧到新,每项含 high/low/close)计算 ATR(period),返回最后一个 ATR 值。"""
|
||||
if not klines or len(klines) < period + 1:
|
||||
return None
|
||||
tr_list = []
|
||||
for i in range(1, len(klines)):
|
||||
high, low = klines[i]['high'], klines[i]['low']
|
||||
prev_close = klines[i - 1]['close']
|
||||
tr = max(high - low, abs(high - prev_close), abs(low - prev_close))
|
||||
tr_list.append(tr)
|
||||
if len(tr_list) < period:
|
||||
return None
|
||||
# ATR = EMA(TR, period)
|
||||
alpha = 2.0 / (period + 1)
|
||||
atr = sum(tr_list[:period]) / period
|
||||
for i in range(period, len(tr_list)):
|
||||
atr = alpha * tr_list[i] + (1 - alpha) * atr
|
||||
return atr
|
||||
|
||||
def get_ema_atr_for_exit(self, kline_series):
|
||||
"""
|
||||
基于已收盘 K 线计算 EMA10、EMA20、ATR(14)。
|
||||
kline_series 最后一根可为当前未收盘 K 线,计算时用倒数第 2~N 根作为已收盘。
|
||||
返回 (ema10, ema20, atr14),任一不足则对应为 None。
|
||||
"""
|
||||
if not kline_series or len(kline_series) < 21:
|
||||
return None, None, None
|
||||
# 用除最后一根外的已收盘 K 线(若只有 21 根则用前 20 根)
|
||||
closed = kline_series[:-1] if len(kline_series) >= 21 else kline_series[:20]
|
||||
closes = [k['close'] for k in closed]
|
||||
ema10 = self._ema(closes, 10)
|
||||
ema20 = self._ema(closes, 20)
|
||||
atr14 = self._atr(closed, 14)
|
||||
return ema10, ema20, atr14
|
||||
|
||||
def get_current_price(self):
|
||||
"""获取当前最新价格"""
|
||||
try:
|
||||
end_time = int(time.time())
|
||||
response = self.contractAPI.get_kline(
|
||||
contract_symbol=self.contract_symbol,
|
||||
step=1, # 1分钟
|
||||
start_time=end_time - 3600 * 1, # 取最近1小时
|
||||
end_time=end_time
|
||||
)[0]
|
||||
if response['code'] == 1000:
|
||||
return float(response['data'][-1]["close_price"])
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"获取价格异常: {e}")
|
||||
return None
|
||||
|
||||
def get_available_balance(self):
|
||||
"""获取合约账户可用USDT余额"""
|
||||
try:
|
||||
response = self.contractAPI.get_assets_detail()[0]
|
||||
if response['code'] == 1000:
|
||||
data = response['data']
|
||||
if isinstance(data, dict):
|
||||
return float(data.get('available_balance', 0))
|
||||
elif isinstance(data, list):
|
||||
for asset in data:
|
||||
if asset.get('currency') == 'USDT':
|
||||
return float(asset.get('available_balance', 0))
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"余额查询异常: {e}")
|
||||
return None
|
||||
|
||||
def get_position_status(self):
|
||||
"""获取当前持仓方向"""
|
||||
try:
|
||||
response = self.contractAPI.get_position(contract_symbol=self.contract_symbol)[0]
|
||||
if response['code'] == 1000:
|
||||
positions = response['data']
|
||||
if not positions:
|
||||
self.start = 0
|
||||
self.open_avg_price = None
|
||||
self.current_amount = None
|
||||
self.unrealized_pnl = None
|
||||
return True
|
||||
pos = positions[0]
|
||||
self.start = 1 if pos['position_type'] == 1 else -1
|
||||
self.open_avg_price = float(pos['open_avg_price'])
|
||||
self.current_amount = float(pos['current_amount'])
|
||||
self.position_cross = pos["position_cross"]
|
||||
# 直接从API获取未实现盈亏(Bitmart返回的是 unrealized_value 字段)
|
||||
self.unrealized_pnl = float(pos.get('unrealized_value', 0))
|
||||
logger.debug(f"持仓详情: 方向={self.start}, 开仓均价={self.open_avg_price}, "
|
||||
f"持仓量={self.current_amount}, 未实现盈亏={self.unrealized_pnl:.2f}")
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"持仓查询异常: {e}")
|
||||
return False
|
||||
|
||||
def get_unrealized_pnl_usd(self):
|
||||
"""
|
||||
获取当前持仓未实现盈亏(美元),直接使用API返回值
|
||||
"""
|
||||
if self.start == 0 or self.unrealized_pnl is None:
|
||||
return None
|
||||
return self.unrealized_pnl
|
||||
|
||||
def set_leverage(self):
|
||||
"""程序启动时设置全仓 + 高杠杆"""
|
||||
try:
|
||||
response = self.contractAPI.post_submit_leverage(
|
||||
contract_symbol=self.contract_symbol,
|
||||
leverage=self.leverage,
|
||||
open_type=self.open_type
|
||||
)[0]
|
||||
if response['code'] == 1000:
|
||||
logger.success(f"全仓模式 + {self.leverage}x 杠杆设置成功")
|
||||
return True
|
||||
else:
|
||||
logger.error(f"杠杆设置失败: {response}")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"设置杠杆异常: {e}")
|
||||
return False
|
||||
|
||||
def openBrowser(self):
|
||||
"""打开 TGE 对应浏览器实例"""
|
||||
try:
|
||||
bit_port = openBrowser(id=self.bit_id)
|
||||
co = ChromiumOptions()
|
||||
co.set_local_port(port=bit_port)
|
||||
self.page = ChromiumPage(addr_or_opts=co)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def click_safe(self, xpath, sleep=0.5):
|
||||
"""安全点击"""
|
||||
try:
|
||||
ele = self.page.ele(xpath)
|
||||
if not ele:
|
||||
return False
|
||||
# ele.scroll.to_see(center=True)
|
||||
# time.sleep(sleep)
|
||||
ele.click(by_js=True)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def 平仓(self):
|
||||
"""平仓操作"""
|
||||
self.click_safe('x://span[normalize-space(text()) ="市价"]')
|
||||
|
||||
def 开单(self, marketPriceLongOrder=0, limitPriceShortOrder=0, size=None, price=None):
|
||||
"""
|
||||
marketPriceLongOrder 市价做多或者做空,1是做多,-1是做空
|
||||
limitPriceShortOrder 限价做多或者做空
|
||||
"""
|
||||
if marketPriceLongOrder == -1:
|
||||
# self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
# self.page.ele('x://*[@id="size_0"]').input(vals=size, clear=True)
|
||||
self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]')
|
||||
elif marketPriceLongOrder == 1:
|
||||
# self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
# self.page.ele('x://*[@id="size_0"]').input(vals=size, clear=True)
|
||||
self.click_safe('x://span[normalize-space(text()) ="买入/做多"]')
|
||||
|
||||
if limitPriceShortOrder == -1:
|
||||
self.click_safe('x://button[normalize-space(text()) ="限价"]')
|
||||
self.page.ele('x://*[@id="price_0"]').input(vals=price, clear=True)
|
||||
time.sleep(1)
|
||||
self.page.ele('x://*[@id="size_0"]').input(1)
|
||||
self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]')
|
||||
elif limitPriceShortOrder == 1:
|
||||
self.click_safe('x://button[normalize-space(text()) ="限价"]')
|
||||
self.page.ele('x://*[@id="price_0"]').input(vals=price, clear=True)
|
||||
time.sleep(1)
|
||||
self.page.ele('x://*[@id="size_0"]').input(1)
|
||||
self.click_safe('x://span[normalize-space(text()) ="买入/做多"]')
|
||||
|
||||
def ding(self, text, error=False):
|
||||
"""日志通知"""
|
||||
if error:
|
||||
logger.error(text)
|
||||
else:
|
||||
logger.info(text)
|
||||
|
||||
def calculate_entity(self, kline):
|
||||
"""计算K线实体大小(绝对值)"""
|
||||
return abs(kline['close'] - kline['open'])
|
||||
|
||||
def calculate_upper_shadow(self, kline):
|
||||
"""计算上阴线(上影线)涨幅百分比"""
|
||||
# 上阴线 = (最高价 - max(开盘价, 收盘价)) / max(开盘价, 收盘价)
|
||||
body_top = max(kline['open'], kline['close'])
|
||||
if body_top == 0:
|
||||
return 0
|
||||
return (kline['high'] - body_top) / body_top * 100
|
||||
|
||||
def calculate_lower_shadow(self, kline):
|
||||
"""计算下阴线(下影线)跌幅百分比"""
|
||||
# 下阴线 = (min(开盘价, 收盘价) - 最低价) / min(开盘价, 收盘价)
|
||||
body_bottom = min(kline['open'], kline['close'])
|
||||
if body_bottom == 0:
|
||||
return 0
|
||||
return (body_bottom - kline['low']) / body_bottom * 100
|
||||
|
||||
def get_entity_edge(self, kline):
|
||||
"""获取K线实体边(收盘价或开盘价,取决于是阳线还是阴线)"""
|
||||
# 阳线(收盘>开盘):实体上边=收盘价,实体下边=开盘价
|
||||
# 阴线(收盘<开盘):实体上边=开盘价,实体下边=收盘价
|
||||
return {
|
||||
'upper': max(kline['open'], kline['close']), # 实体上边
|
||||
'lower': min(kline['open'], kline['close']) # 实体下边
|
||||
}
|
||||
|
||||
def check_signal(self, current_price, prev_kline, current_kline):
|
||||
"""
|
||||
检查交易信号
|
||||
返回: ('long', trigger_price) / ('short', trigger_price) / None
|
||||
"""
|
||||
# 计算上一根K线实体
|
||||
prev_entity = self.calculate_entity(prev_kline)
|
||||
|
||||
# 实体过小不交易(实体 < 0.1)
|
||||
if prev_entity < 0.1:
|
||||
logger.info(LOG_PRICE + f"上一根K线实体过小: {prev_entity:.4f},跳过信号检测")
|
||||
return None
|
||||
|
||||
# 获取上一根K线的实体上下边
|
||||
prev_entity_edge = self.get_entity_edge(prev_kline)
|
||||
prev_entity_upper = prev_entity_edge['upper'] # 实体上边
|
||||
prev_entity_lower = prev_entity_edge['lower'] # 实体下边
|
||||
|
||||
# 优化:以下两种情况以当前这根的开盘价作为计算基准
|
||||
# 1) 上一根阳线 且 当前开盘价 > 上一根收盘价(跳空高开)
|
||||
# 2) 上一根阴线 且 当前开盘价 < 上一根收盘价(跳空低开)
|
||||
prev_is_bullish_for_calc = prev_kline['close'] > prev_kline['open']
|
||||
prev_is_bearish_for_calc = prev_kline['close'] < prev_kline['open']
|
||||
current_open_above_prev_close = current_kline['open'] > prev_kline['close']
|
||||
current_open_below_prev_close = current_kline['open'] < prev_kline['close']
|
||||
use_current_open_as_base = (prev_is_bullish_for_calc and current_open_above_prev_close) or (prev_is_bearish_for_calc and current_open_below_prev_close)
|
||||
|
||||
if use_current_open_as_base:
|
||||
# 以当前K线开盘价为基准计算(跳空时用当前开盘价参与计算)
|
||||
calc_lower = current_kline['open']
|
||||
calc_upper = current_kline['open'] # 同一基准,上下四分之一对称
|
||||
long_trigger = calc_lower + prev_entity / 4
|
||||
short_trigger = calc_upper - prev_entity / 4
|
||||
long_breakout = calc_upper + prev_entity / 4
|
||||
short_breakout = calc_lower - prev_entity / 4
|
||||
else:
|
||||
# 原有计算方式
|
||||
long_trigger = prev_entity_lower + prev_entity / 4 # 做多触发价 = 实体下边 + 实体/4(下四分之一处)
|
||||
short_trigger = prev_entity_upper - prev_entity / 4 # 做空触发价 = 实体上边 - 实体/4(上四分之一处)
|
||||
long_breakout = prev_entity_upper + prev_entity / 4 # 做多突破价 = 实体上边 + 实体/4
|
||||
short_breakout = prev_entity_lower - prev_entity / 4 # 做空突破价 = 实体下边 - 实体/4
|
||||
|
||||
# 上一根阴线 + 当前阳线:做多形态,不按上一根K线上三分之一做空
|
||||
prev_is_bearish = prev_kline['close'] < prev_kline['open']
|
||||
current_is_bullish = current_kline['close'] > current_kline['open']
|
||||
skip_short_by_upper_third = prev_is_bearish and current_is_bullish
|
||||
# 上一根阳线 + 当前阴线:做空形态,不按上一根K线下三分之一做多
|
||||
prev_is_bullish = prev_kline['close'] > prev_kline['open']
|
||||
current_is_bearish = current_kline['close'] < current_kline['open']
|
||||
skip_long_by_lower_third = prev_is_bullish and current_is_bearish
|
||||
|
||||
if use_current_open_as_base:
|
||||
if prev_is_bullish_for_calc and current_open_above_prev_close:
|
||||
logger.info(LOG_PRICE + f"上一根阳线且当前开盘价({current_kline['open']:.2f})>上一根收盘价({prev_kline['close']:.2f}),以当前开盘价为基准计算")
|
||||
else:
|
||||
logger.info(LOG_PRICE + f"上一根阴线且当前开盘价({current_kline['open']:.2f})<上一根收盘价({prev_kline['close']:.2f}),以当前开盘价为基准计算")
|
||||
logger.info(LOG_PRICE + f"当前价: {current_price:.2f} | 上一根实体: {prev_entity:.4f} | 实体上边: {prev_entity_upper:.2f} 下边: {prev_entity_lower:.2f}")
|
||||
logger.info(LOG_PRICE + f"做多触发(下1/4): {long_trigger:.2f} | 做空触发(上1/4): {short_trigger:.2f} | 突破做多: {long_breakout:.2f} | 突破做空: {short_breakout:.2f}")
|
||||
if skip_short_by_upper_third:
|
||||
logger.info(LOG_PRICE + "上一根阴+当前阳(做多形态),不按上1/4做空")
|
||||
if skip_long_by_lower_third:
|
||||
logger.info(LOG_PRICE + "上一根阳+当前阴(做空形态),不按下1/4做多")
|
||||
|
||||
self._display_triggers = {"long_trigger": long_trigger, "short_trigger": short_trigger, "long_breakout": long_breakout, "short_breakout": short_breakout}
|
||||
|
||||
# 无持仓时检查开仓信号
|
||||
if self.start == 0:
|
||||
if current_price >= long_breakout and not skip_long_by_lower_third:
|
||||
logger.info(LOG_SIGNAL + f"触发做多 | 价 {current_price:.2f} >= 突破价 {long_breakout:.2f}")
|
||||
return ('long', long_breakout)
|
||||
elif current_price <= short_breakout and not skip_short_by_upper_third:
|
||||
logger.info(LOG_SIGNAL + f"触发做空 | 价 {current_price:.2f} <= 突破价 {short_breakout:.2f}")
|
||||
return ('short', short_breakout)
|
||||
|
||||
# 持仓时检查反手信号
|
||||
elif self.start == 1: # 持多仓
|
||||
if current_price <= short_trigger and not skip_short_by_upper_third:
|
||||
logger.info(LOG_SIGNAL + f"持多→反手做空 | 价 {current_price:.2f} <= 触发价 {short_trigger:.2f}")
|
||||
return ('reverse_short', short_trigger)
|
||||
upper_shadow_pct = self.calculate_upper_shadow(prev_kline)
|
||||
if upper_shadow_pct > 0.01 and current_price <= prev_entity_lower:
|
||||
logger.info(LOG_SIGNAL + f"持多→反手做空 | 上阴线 {upper_shadow_pct:.4f}% 价<=实体下边 {prev_entity_lower:.2f}")
|
||||
return ('reverse_short', prev_entity_lower)
|
||||
|
||||
elif self.start == -1: # 持空仓
|
||||
if current_price >= long_trigger and not skip_long_by_lower_third:
|
||||
logger.info(LOG_SIGNAL + f"持空→反手做多 | 价 {current_price:.2f} >= 触发价 {long_trigger:.2f}")
|
||||
return ('reverse_long', long_trigger)
|
||||
lower_shadow_pct = self.calculate_lower_shadow(prev_kline)
|
||||
if lower_shadow_pct > 0.01 and current_price >= prev_entity_upper:
|
||||
logger.info(LOG_SIGNAL + f"持空→反手做多 | 下阴线 {lower_shadow_pct:.4f}% 价>=实体上边 {prev_entity_upper:.2f}")
|
||||
return ('reverse_long', prev_entity_upper)
|
||||
|
||||
return None
|
||||
|
||||
def can_open(self, current_kline_id):
|
||||
"""开仓前过滤:仅开仓冷却时间。单根 K 线符合规则可多次开仓。"""
|
||||
now = time.time()
|
||||
if self.last_open_time is not None and now - self.last_open_time < self.open_cooldown_seconds:
|
||||
remain = self.open_cooldown_seconds - (now - self.last_open_time)
|
||||
logger.info(LOG_SYSTEM + f"开仓冷却中,剩余 {remain:.0f} 秒")
|
||||
return False
|
||||
return True
|
||||
|
||||
def can_reverse(self, current_price, trigger_price):
|
||||
"""反手前过滤:冷却时间 + 最小价差"""
|
||||
now = time.time()
|
||||
if self.last_reverse_time and now - self.last_reverse_time < self.reverse_cooldown_seconds:
|
||||
remain = self.reverse_cooldown_seconds - (now - self.last_reverse_time)
|
||||
logger.info(LOG_SYSTEM + f"反手冷却中,剩余 {remain:.0f} 秒")
|
||||
return False
|
||||
|
||||
if trigger_price and trigger_price > 0:
|
||||
move_pct = abs(current_price - trigger_price) / trigger_price * 100
|
||||
if move_pct < self.reverse_min_move_pct:
|
||||
logger.info(LOG_SYSTEM + f"反手价差不足: {move_pct:.4f}% < {self.reverse_min_move_pct}%")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def verify_no_position(self, max_retries=5, retry_interval=3):
|
||||
"""
|
||||
验证当前无持仓
|
||||
返回: True 表示无持仓可以开仓,False 表示有持仓不能开仓
|
||||
"""
|
||||
for i in range(max_retries):
|
||||
if self.get_position_status():
|
||||
if self.start == 0:
|
||||
logger.info(LOG_POSITION + "确认无持仓,可以开仓")
|
||||
return True
|
||||
else:
|
||||
logger.warning(
|
||||
f"仍有持仓 (方向: {self.start}),等待 {retry_interval} 秒后重试 ({i + 1}/{max_retries})")
|
||||
time.sleep(retry_interval)
|
||||
else:
|
||||
logger.warning(f"查询持仓状态失败,等待 {retry_interval} 秒后重试 ({i + 1}/{max_retries})")
|
||||
time.sleep(retry_interval)
|
||||
|
||||
logger.error(f"经过 {max_retries} 次重试仍有持仓或查询失败,放弃开仓")
|
||||
return False
|
||||
|
||||
def verify_position_direction(self, expected_direction):
|
||||
"""
|
||||
验证当前持仓方向是否与预期一致
|
||||
expected_direction: 1 多仓, -1 空仓
|
||||
返回: True 表示持仓方向正确,False 表示不正确
|
||||
"""
|
||||
if self.get_position_status():
|
||||
if self.start == expected_direction:
|
||||
logger.info(LOG_POSITION + f"持仓方向验证成功: {self.start}")
|
||||
return True
|
||||
else:
|
||||
logger.warning(f"持仓方向不符: 期望 {expected_direction}, 实际 {self.start}")
|
||||
return False
|
||||
else:
|
||||
logger.error("查询持仓状态失败")
|
||||
return False
|
||||
|
||||
def execute_trade(self, signal, size=None):
|
||||
"""执行交易。size 不传或为 None 时使用 default_order_size。"""
|
||||
signal_type, trigger_price = signal
|
||||
size = self.default_order_size if size is None else size
|
||||
|
||||
if signal_type == 'long':
|
||||
# 开多前先确认无持仓
|
||||
logger.info(LOG_POSITION + f"准备开多,触发价: {trigger_price:.2f}")
|
||||
if not self.get_position_status():
|
||||
logger.error("开仓前查询持仓状态失败,放弃开仓")
|
||||
return False
|
||||
if self.start != 0:
|
||||
logger.warning(f"开多前发现已有持仓 (方向: {self.start}),放弃开仓避免双向持仓")
|
||||
return False
|
||||
|
||||
logger.info(LOG_POSITION + "确认无持仓,执行开多")
|
||||
self.开单(marketPriceLongOrder=1, size=size)
|
||||
time.sleep(3) # 等待订单执行
|
||||
|
||||
# 验证开仓是否成功
|
||||
if self.verify_position_direction(1):
|
||||
self.max_unrealized_pnl_seen = None # 新仓位重置移动止损记录
|
||||
self.last_open_time = time.time()
|
||||
self.last_open_kline_id = getattr(self, "_current_kline_id_for_open", None)
|
||||
logger.success(LOG_POSITION + "开多成功")
|
||||
return True
|
||||
else:
|
||||
logger.error("开多后持仓验证失败")
|
||||
return False
|
||||
|
||||
elif signal_type == 'short':
|
||||
# 开空前先确认无持仓
|
||||
logger.info(LOG_POSITION + f"准备开空,触发价: {trigger_price:.2f}")
|
||||
if not self.get_position_status():
|
||||
logger.error("开仓前查询持仓状态失败,放弃开仓")
|
||||
return False
|
||||
if self.start != 0:
|
||||
logger.warning(f"开空前发现已有持仓 (方向: {self.start}),放弃开仓避免双向持仓")
|
||||
return False
|
||||
|
||||
logger.info(LOG_POSITION + "确认无持仓,执行开空")
|
||||
self.开单(marketPriceLongOrder=-1, size=size)
|
||||
time.sleep(3) # 等待订单执行
|
||||
|
||||
# 验证开仓是否成功
|
||||
if self.verify_position_direction(-1):
|
||||
self.max_unrealized_pnl_seen = None # 新仓位重置移动止损记录
|
||||
self.last_open_time = time.time()
|
||||
self.last_open_kline_id = getattr(self, "_current_kline_id_for_open", None)
|
||||
logger.success(LOG_POSITION + "开空成功")
|
||||
return True
|
||||
else:
|
||||
logger.error("开空后持仓验证失败")
|
||||
return False
|
||||
|
||||
elif signal_type == 'reverse_long':
|
||||
# 平空 + 开多(反手做多):先平仓,确认无仓后再开多,避免双向持仓
|
||||
logger.info(LOG_POSITION + f"执行反手做多,触发价: {trigger_price:.2f}")
|
||||
self.平仓()
|
||||
time.sleep(1) # 给交易所处理平仓的时间
|
||||
# 轮询确认已无持仓再开多(最多等约 10 秒)
|
||||
for _ in range(10):
|
||||
if self.get_position_status() and self.start == 0:
|
||||
break
|
||||
time.sleep(1)
|
||||
if self.start != 0:
|
||||
logger.warning("反手做多:平仓后仍有持仓,放弃本次开多")
|
||||
return False
|
||||
logger.info(LOG_POSITION + "已确认无持仓,执行开多")
|
||||
self.开单(marketPriceLongOrder=1, size=size)
|
||||
time.sleep(3)
|
||||
|
||||
if self.verify_position_direction(1):
|
||||
self.max_unrealized_pnl_seen = None
|
||||
logger.success(LOG_POSITION + "反手做多成功")
|
||||
self.last_reverse_time = time.time()
|
||||
time.sleep(20)
|
||||
return True
|
||||
else:
|
||||
logger.error("反手做多后持仓验证失败")
|
||||
return False
|
||||
|
||||
elif signal_type == 'reverse_short':
|
||||
# 平多 + 开空(反手做空):先平仓,确认无仓后再开空
|
||||
logger.info(LOG_POSITION + f"执行反手做空,触发价: {trigger_price:.2f}")
|
||||
self.平仓()
|
||||
time.sleep(1)
|
||||
for _ in range(10):
|
||||
if self.get_position_status() and self.start == 0:
|
||||
break
|
||||
time.sleep(1)
|
||||
if self.start != 0:
|
||||
logger.warning("反手做空:平仓后仍有持仓,放弃本次开空")
|
||||
return False
|
||||
logger.info(LOG_POSITION + "已确认无持仓,执行开空")
|
||||
self.开单(marketPriceLongOrder=-1, size=size)
|
||||
time.sleep(3)
|
||||
|
||||
if self.verify_position_direction(-1):
|
||||
self.max_unrealized_pnl_seen = None
|
||||
logger.success(LOG_POSITION + "反手做空成功")
|
||||
self.last_reverse_time = time.time()
|
||||
time.sleep(20)
|
||||
return True
|
||||
else:
|
||||
logger.error("反手做空后持仓验证失败")
|
||||
return False
|
||||
|
||||
return False
|
||||
|
||||
def action(self):
|
||||
"""主循环"""
|
||||
|
||||
logger.info(LOG_SYSTEM + "开始运行四分之一策略交易...")
|
||||
|
||||
# 启动时设置全仓高杠杆
|
||||
if not self.set_leverage():
|
||||
logger.error("杠杆设置失败,程序继续运行但可能下单失败")
|
||||
return
|
||||
|
||||
page_start = True
|
||||
if USE_RICH_DASHBOARD:
|
||||
self._display_logs = []
|
||||
logger.remove()
|
||||
def _sink(msg):
|
||||
self._display_logs.append(msg.record["message"])
|
||||
if len(self._display_logs) > 50:
|
||||
self._display_logs.pop(0)
|
||||
logger.add(_sink, format="{message}")
|
||||
|
||||
live = None
|
||||
if USE_RICH_DASHBOARD:
|
||||
live = Live(console=console, refresh_per_second=8, screen=True)
|
||||
live.start()
|
||||
try:
|
||||
while True:
|
||||
if live is not None:
|
||||
try:
|
||||
live.update(build_dashboard_layout(self._display_state, self._display_logs))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if page_start:
|
||||
# 打开浏览器
|
||||
for i in range(5):
|
||||
if self.openBrowser():
|
||||
logger.info(LOG_SYSTEM + "浏览器打开成功")
|
||||
break
|
||||
else:
|
||||
self.ding("打开浏览器失败!", error=True)
|
||||
return
|
||||
|
||||
# 进入交易页面
|
||||
self.page.get("https://derivatives.bitmart.com/zh-CN/futures/ETHUSDT")
|
||||
self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
|
||||
self.page.ele('x://*[@id="size_0"]').input(vals=25, clear=True)
|
||||
|
||||
page_start = False
|
||||
|
||||
try:
|
||||
# 1. 获取K线数据(当前K线和上一根K线)
|
||||
prev_kline, current_kline = self.get_klines()
|
||||
if not prev_kline or not current_kline:
|
||||
logger.warning(LOG_SYSTEM + "获取K线失败,等待重试...")
|
||||
time.sleep(5)
|
||||
continue
|
||||
|
||||
# 记录进入新的K线(分块分隔,便于阅读)
|
||||
current_kline_time = current_kline['id']
|
||||
if self.last_kline_time != current_kline_time:
|
||||
self.last_kline_time = current_kline_time
|
||||
logger.info("")
|
||||
log_kline_header(current_kline_time)
|
||||
|
||||
# 2. 获取当前价格
|
||||
current_price = self.get_current_price()
|
||||
if not current_price:
|
||||
logger.warning(LOG_SYSTEM + "获取价格失败,等待重试...")
|
||||
time.sleep(2)
|
||||
continue
|
||||
|
||||
# 3. 每次循环都通过SDK获取真实持仓状态(避免状态不同步导致双向持仓)
|
||||
if not self.get_position_status():
|
||||
logger.warning(LOG_SYSTEM + "获取持仓状态失败,等待重试...")
|
||||
time.sleep(2)
|
||||
continue
|
||||
|
||||
logger.debug(f"当前持仓状态: {self.start} (0=无, 1=多, -1=空)")
|
||||
|
||||
# 更新仪表盘左侧数据(供 Rich 展示)
|
||||
try:
|
||||
self._display_state["price"] = current_price
|
||||
self._display_state["position"] = self.start
|
||||
self._display_state["kline_id"] = current_kline_time
|
||||
self._display_state["unrealized_pnl"] = self.get_unrealized_pnl_usd()
|
||||
kline_series = self.get_klines_series(35)
|
||||
ema10, ema20, atr14 = self.get_ema_atr_for_exit(kline_series)
|
||||
self._display_state["ema10"] = ema10
|
||||
self._display_state["atr14"] = atr14
|
||||
if getattr(self, "_display_triggers", None):
|
||||
self._display_state.update(self._display_triggers)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# 3.5 止损/止盈/移动止损 + EMA/ATR 平仓 + 当前K线从极值回落平仓
|
||||
if self.start != 0:
|
||||
# 当前K线从最高/最低点回落平仓:换线重置跟踪,有持仓时更新本K线内最高/最低价并检查
|
||||
if self._candle_id_for_high_low != current_kline_time:
|
||||
self._candle_high_seen = None
|
||||
self._candle_low_seen = None
|
||||
self._candle_id_for_high_low = current_kline_time
|
||||
# 多头:先更新本 K 线最高价(供 ATR 追踪与后续回落逻辑用)
|
||||
if self.start == 1:
|
||||
self._candle_high_seen = max(self._candle_high_seen or 0, current_price)
|
||||
elif self.start == -1:
|
||||
self._candle_low_seen = min(self._candle_low_seen or float('inf'), current_price)
|
||||
|
||||
# 多单:EMA(10) + ATR(14) 平仓 —— 收盘跌破 EMA10 先平;或从最高价回撤 ≥ 1.1×ATR 平
|
||||
if self.start == 1 and self.use_ema_atr_exit:
|
||||
kline_series = self.get_klines_series(35)
|
||||
ema10, ema20, atr14 = self.get_ema_atr_for_exit(kline_series)
|
||||
if ema10 is not None and current_price < ema10:
|
||||
logger.info(LOG_POSITION + f"多单 EMA10 平仓 | 价 {current_price:.2f} 跌破 EMA10 {ema10:.2f}")
|
||||
self.平仓()
|
||||
self.max_unrealized_pnl_seen = None
|
||||
self._candle_high_seen = None
|
||||
time.sleep(3)
|
||||
continue
|
||||
if atr14 is not None and self._candle_high_seen and (self._candle_high_seen - current_price) >= self.atr_multiplier * atr14:
|
||||
logger.info(LOG_POSITION + f"多单 ATR 追踪止盈 | 最高 {self._candle_high_seen:.2f} 当前 {current_price:.2f} 回撤≥{self.atr_multiplier}×ATR={atr14:.2f}")
|
||||
self.平仓()
|
||||
self.max_unrealized_pnl_seen = None
|
||||
self._candle_high_seen = None
|
||||
time.sleep(3)
|
||||
continue
|
||||
|
||||
# 空单:EMA(10) + ATR(14) 平仓 —— 收盘涨破 EMA10 先平;或从最低价反弹 ≥ 1.1×ATR 平
|
||||
if self.start == -1 and self.use_ema_atr_exit:
|
||||
kline_series = self.get_klines_series(35)
|
||||
ema10, ema20, atr14 = self.get_ema_atr_for_exit(kline_series)
|
||||
if ema10 is not None and current_price > ema10:
|
||||
logger.info(LOG_POSITION + f"空单 EMA10 平仓 | 价 {current_price:.2f} 涨破 EMA10 {ema10:.2f}")
|
||||
self.平仓()
|
||||
self.max_unrealized_pnl_seen = None
|
||||
self._candle_low_seen = None
|
||||
time.sleep(3)
|
||||
continue
|
||||
if atr14 is not None and self._candle_low_seen and (current_price - self._candle_low_seen) >= self.atr_multiplier * atr14:
|
||||
logger.info(LOG_POSITION + f"空单 ATR 追踪止盈 | 最低 {self._candle_low_seen:.2f} 当前 {current_price:.2f} 反弹≥{self.atr_multiplier}×ATR={atr14:.2f}")
|
||||
self.平仓()
|
||||
self.max_unrealized_pnl_seen = None
|
||||
self._candle_low_seen = None
|
||||
time.sleep(3)
|
||||
continue
|
||||
|
||||
use_fixed = self.drop_from_high_mode == 'fixed' and self.drop_from_high_to_close and self.drop_from_high_to_close > 0
|
||||
use_pct = self.drop_from_high_mode == 'pct_retrace'
|
||||
if use_fixed or use_pct:
|
||||
if self.start == 1: # 多头:最高价已在上面更新,这里只做回落判断
|
||||
do_close = False
|
||||
if use_fixed and self._candle_high_seen and current_price <= self._candle_high_seen - self.drop_from_high_to_close:
|
||||
do_close = True
|
||||
reason = f"固定回落 {self.drop_from_high_to_close}"
|
||||
elif use_pct and self._candle_high_seen and current_kline.get('open'):
|
||||
candle_open = float(current_kline['open'])
|
||||
rise_pct = (self._candle_high_seen - candle_open) / candle_open * 100 if candle_open > 0 else 0
|
||||
if rise_pct >= self.min_rise_pct_to_activate:
|
||||
drop_trigger_pct = max(self.min_drop_pct_from_high, rise_pct * self.retrace_ratio)
|
||||
drop_pct = (self._candle_high_seen - current_price) / self._candle_high_seen * 100 if self._candle_high_seen else 0
|
||||
if drop_pct >= drop_trigger_pct:
|
||||
do_close = True
|
||||
reason = f"涨幅 {rise_pct:.3f}% → 允许回撤 {drop_trigger_pct:.3f}%,实际回撤 {drop_pct:.3f}%"
|
||||
else:
|
||||
pass # 涨幅不足,不启用动态回撤
|
||||
if do_close:
|
||||
logger.info(LOG_POSITION + f"多单K线回落平仓 | 最高 {self._candle_high_seen:.2f} 当前 {current_price:.2f} | {reason}")
|
||||
self.平仓()
|
||||
self.max_unrealized_pnl_seen = None
|
||||
self._candle_high_seen = None
|
||||
time.sleep(3)
|
||||
continue
|
||||
elif self.start == -1: # 空头:跟踪当前K线最低价,从最低点反弹超过阈值则平仓
|
||||
self._candle_low_seen = min(self._candle_low_seen or float('inf'), current_price)
|
||||
do_close = False
|
||||
if use_fixed and self._candle_low_seen and current_price >= self._candle_low_seen + self.drop_from_high_to_close:
|
||||
do_close = True
|
||||
reason = f"固定反弹 {self.drop_from_high_to_close}"
|
||||
elif use_pct and self._candle_low_seen and current_kline.get('open'):
|
||||
candle_open = float(current_kline['open'])
|
||||
rise_pct = (candle_open - self._candle_low_seen) / candle_open * 100 if candle_open > 0 else 0 # 对空头是“跌幅”
|
||||
if rise_pct >= self.min_rise_pct_to_activate:
|
||||
drop_trigger_pct = max(self.min_drop_pct_from_high, rise_pct * self.retrace_ratio)
|
||||
bounce_pct = (current_price - self._candle_low_seen) / self._candle_low_seen * 100 if self._candle_low_seen else 0
|
||||
if bounce_pct >= drop_trigger_pct:
|
||||
do_close = True
|
||||
reason = f"跌幅 {rise_pct:.3f}% → 允许反弹 {drop_trigger_pct:.3f}%,实际反弹 {bounce_pct:.3f}%"
|
||||
if do_close:
|
||||
logger.info(LOG_POSITION + f"空单K线反弹平仓 | 最低 {self._candle_low_seen:.2f} 当前 {current_price:.2f} | {reason}")
|
||||
self.平仓()
|
||||
self.max_unrealized_pnl_seen = None
|
||||
self._candle_low_seen = None
|
||||
time.sleep(3)
|
||||
continue
|
||||
|
||||
pnl_usd = self.get_unrealized_pnl_usd()
|
||||
if pnl_usd is not None:
|
||||
# 固定止损:亏损达到 3 美元平仓
|
||||
if pnl_usd <= self.stop_loss_usd:
|
||||
logger.info(LOG_POSITION + f"固定止损平仓 | 亏损 {pnl_usd:.2f} 美元")
|
||||
self.平仓()
|
||||
self.max_unrealized_pnl_seen = None
|
||||
time.sleep(3)
|
||||
continue
|
||||
# 更新持仓期间最大盈利(用于移动止损)
|
||||
if self.max_unrealized_pnl_seen is None:
|
||||
self.max_unrealized_pnl_seen = pnl_usd
|
||||
else:
|
||||
self.max_unrealized_pnl_seen = max(self.max_unrealized_pnl_seen, pnl_usd)
|
||||
# 移动止损:盈利曾达到 activation 后,从最高盈利回撤 trailing_distance 则平仓
|
||||
if self.max_unrealized_pnl_seen >= self.trailing_activation_usd:
|
||||
if pnl_usd < self.max_unrealized_pnl_seen - self.trailing_distance_usd:
|
||||
logger.info(LOG_POSITION + f"移动止损平仓 | 盈利 {pnl_usd:.2f} 从最高 {self.max_unrealized_pnl_seen:.2f} 回撤≥{self.trailing_distance_usd}$")
|
||||
self.平仓()
|
||||
self.max_unrealized_pnl_seen = None
|
||||
time.sleep(3)
|
||||
continue
|
||||
# 4. 检查信号
|
||||
signal = self.check_signal(current_price, prev_kline, current_kline)
|
||||
|
||||
# 5. 反手过滤:冷却时间 + 最小价差
|
||||
if signal and signal[0].startswith('reverse_'):
|
||||
if not self.can_reverse(current_price, signal[1]):
|
||||
signal = None
|
||||
|
||||
# 5.5 开仓频率过滤:仅冷却时间,单根 K 线符合规则可多次开仓
|
||||
if signal and signal[0] in ('long', 'short'):
|
||||
if not self.can_open(current_kline_time):
|
||||
signal = None
|
||||
else:
|
||||
self._current_kline_id_for_open = current_kline_time # 供 execute_trade 成功后记录
|
||||
|
||||
# 6. 有信号则执行交易
|
||||
if signal:
|
||||
trade_success = self.execute_trade(signal)
|
||||
if trade_success:
|
||||
logger.success(LOG_POSITION + f"交易执行完成: {signal[0]} | 当前持仓: {self.start}")
|
||||
page_start = True
|
||||
else:
|
||||
logger.warning(f"交易执行失败或被阻止: {signal[0]}")
|
||||
|
||||
# 短暂等待后继续循环(同一根K线遇到信号就操作)
|
||||
time.sleep(0.1)
|
||||
|
||||
if page_start:
|
||||
self.page.close()
|
||||
time.sleep(5)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logger.info(LOG_SYSTEM + "用户中断,程序退出")
|
||||
break
|
||||
except Exception as e:
|
||||
logger.error(f"主循环异常: {e}")
|
||||
time.sleep(5)
|
||||
finally:
|
||||
if live is not None:
|
||||
try:
|
||||
live.stop()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if USE_RICH_DASHBOARD:
|
||||
logger.remove()
|
||||
logger.add(sys.stderr, format="{message}")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
BitmartFuturesTransaction(bit_id="f2320f57e24c45529a009e1541e25961").action()
|
||||
|
||||
@@ -1,745 +0,0 @@
|
||||
"""
|
||||
BitMart 五分之一回归策略回测(精准版)
|
||||
使用3分钟K线周期计算触发价格,1分钟K线判断触发顺序
|
||||
|
||||
========== 策略规则 ==========
|
||||
1. 触发价格计算(基于有效的前一根K线,实体>=0.1):
|
||||
- 做多触发价格 = 收盘价 + 实体/5(从收盘价往上涨1/5)
|
||||
- 做空触发价格 = 收盘价 - 实体/5(从收盘价往下跌1/5)
|
||||
|
||||
2. 信号触发条件:
|
||||
- 当前K线最高价 >= 做多触发价格 → 做多信号
|
||||
- 当前K线最低价 <= 做空触发价格 → 做空信号
|
||||
|
||||
3. 执行逻辑:
|
||||
- 做多时遇到做空信号 -> 平多并反手开空
|
||||
- 做空时遇到做多信号 -> 平空并反手开多
|
||||
- 同一根3分钟K线内只交易一次
|
||||
|
||||
4. 精准判断(使用1分钟K线):
|
||||
- 当一根3分钟K线同时触及做多和做空价格时
|
||||
- 使用该3分钟K线对应的3根1分钟K线来判断哪个方向先被触发
|
||||
- 这样可以更精准地还原真实交易场景
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import calendar
|
||||
from pathlib import Path
|
||||
from typing import List, Dict, Optional
|
||||
from loguru import logger
|
||||
from peewee import *
|
||||
|
||||
# 数据库配置
|
||||
DB_PATH = Path(__file__).parent.parent / 'models' / 'database.db'
|
||||
db = SqliteDatabase(str(DB_PATH))
|
||||
|
||||
|
||||
# ========================= 数据库模型 =========================
|
||||
|
||||
class BitMartETH1m(Model):
|
||||
"""1分钟K线模型"""
|
||||
id = BigIntegerField(primary_key=True) # 时间戳(毫秒级)
|
||||
open = FloatField(null=True)
|
||||
high = FloatField(null=True)
|
||||
low = FloatField(null=True)
|
||||
close = FloatField(null=True)
|
||||
|
||||
class Meta:
|
||||
database = db
|
||||
table_name = 'bitmart_eth_1m'
|
||||
|
||||
|
||||
class BitMartETH3m(Model):
|
||||
"""3分钟K线模型"""
|
||||
id = BigIntegerField(primary_key=True) # 时间戳(毫秒级)
|
||||
open = FloatField(null=True)
|
||||
high = FloatField(null=True)
|
||||
low = FloatField(null=True)
|
||||
close = FloatField(null=True)
|
||||
|
||||
class Meta:
|
||||
database = db
|
||||
table_name = 'bitmart_eth_3m'
|
||||
|
||||
|
||||
# 连接数据库
|
||||
db.connect(reuse_if_open=True)
|
||||
|
||||
|
||||
# ========================= 工具函数 =========================
|
||||
|
||||
def is_bullish(c):
|
||||
"""判断阳线"""
|
||||
return float(c['close']) > float(c['open'])
|
||||
|
||||
|
||||
def is_bearish(c):
|
||||
"""判断阴线"""
|
||||
return float(c['close']) < float(c['open'])
|
||||
|
||||
|
||||
def get_body_size(candle):
|
||||
"""计算K线实体大小(绝对值)"""
|
||||
return abs(float(candle['open']) - float(candle['close']))
|
||||
|
||||
|
||||
def find_valid_prev_bar(all_data, current_idx, min_body_size=0.1):
|
||||
"""
|
||||
从当前索引往前查找,直到找到实体>=min_body_size的K线
|
||||
返回:(有效K线的索引, K线数据) 或 (None, None)
|
||||
"""
|
||||
if current_idx <= 0:
|
||||
return None, None
|
||||
|
||||
for i in range(current_idx - 1, -1, -1):
|
||||
prev = all_data[i]
|
||||
body_size = get_body_size(prev)
|
||||
if body_size >= min_body_size:
|
||||
return i, prev
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
def get_one_fifth_levels(prev):
|
||||
"""
|
||||
计算前一根K线实体的 1/5 双向触发价格
|
||||
返回:(做多触发价格, 做空触发价格)
|
||||
|
||||
基于收盘价计算(无论阴线阳线):
|
||||
- 做多触发价格 = 收盘价 + 实体/5(从收盘价往上涨1/5实体)
|
||||
- 做空触发价格 = 收盘价 - 实体/5(从收盘价往下跌1/5实体)
|
||||
"""
|
||||
p_open = float(prev['open'])
|
||||
p_close = float(prev['close'])
|
||||
|
||||
body = abs(p_open - p_close)
|
||||
|
||||
if body < 0.001: # 十字星,忽略
|
||||
return None, None
|
||||
|
||||
# 基于收盘价的双向触发价格
|
||||
long_trigger = p_close + body / 5
|
||||
short_trigger = p_close - body / 5
|
||||
|
||||
return long_trigger, short_trigger
|
||||
|
||||
|
||||
def get_3m_data_by_date(date_str: str) -> List[Dict]:
|
||||
"""按天获取3分钟K线数据"""
|
||||
try:
|
||||
target_date = datetime.datetime.strptime(date_str, '%Y-%m-%d')
|
||||
except ValueError:
|
||||
logger.error("日期格式不正确,请使用 YYYY-MM-DD 格式。")
|
||||
return []
|
||||
|
||||
start_ts = int(target_date.timestamp() * 1000)
|
||||
end_ts = int((target_date + datetime.timedelta(days=1)).timestamp() * 1000) - 1
|
||||
|
||||
query = BitMartETH3m.select().where(
|
||||
BitMartETH3m.id.between(start_ts, end_ts)
|
||||
).order_by(BitMartETH3m.id.asc())
|
||||
|
||||
data = [{'id': i.id, 'open': i.open, 'high': i.high, 'low': i.low, 'close': i.close} for i in query]
|
||||
return data
|
||||
|
||||
|
||||
def get_1m_data_by_range(start_ts: int, end_ts: int) -> List[Dict]:
|
||||
"""
|
||||
获取指定时间范围内的1分钟K线数据
|
||||
:param start_ts: 开始时间戳(毫秒)
|
||||
:param end_ts: 结束时间戳(毫秒)
|
||||
:return: 1分钟K线数据列表
|
||||
"""
|
||||
query = BitMartETH1m.select().where(
|
||||
BitMartETH1m.id.between(start_ts, end_ts - 1)
|
||||
).order_by(BitMartETH1m.id.asc())
|
||||
|
||||
data = [{'id': i.id, 'open': i.open, 'high': i.high, 'low': i.low, 'close': i.close} for i in query]
|
||||
return data
|
||||
|
||||
|
||||
def get_1m_data_for_3m_bar(bar_3m: Dict) -> List[Dict]:
|
||||
"""
|
||||
获取3分钟K线对应的3根1分钟K线
|
||||
:param bar_3m: 3分钟K线数据
|
||||
:return: 对应的1分钟K线数据列表(最多3根)
|
||||
"""
|
||||
start_ts = bar_3m['id']
|
||||
end_ts = start_ts + 3 * 60 * 1000 # 3分钟后
|
||||
return get_1m_data_by_range(start_ts, end_ts)
|
||||
|
||||
|
||||
def determine_trigger_order_by_1m(
|
||||
bars_1m: List[Dict],
|
||||
long_trigger: float,
|
||||
short_trigger: float
|
||||
) -> str:
|
||||
"""
|
||||
使用1分钟K线精确判断在3分钟周期内,是先触发做多还是做空
|
||||
|
||||
:param bars_1m: 3根1分钟K线数据
|
||||
:param long_trigger: 做多触发价格
|
||||
:param short_trigger: 做空触发价格
|
||||
:return: 'long', 'short', 或 None
|
||||
"""
|
||||
if not bars_1m:
|
||||
return None
|
||||
|
||||
for bar in bars_1m:
|
||||
high = float(bar['high'])
|
||||
low = float(bar['low'])
|
||||
open_price = float(bar['open'])
|
||||
|
||||
long_triggered = high >= long_trigger
|
||||
short_triggered = low <= short_trigger
|
||||
|
||||
# 如果只触发了一个方向
|
||||
if long_triggered and not short_triggered:
|
||||
return 'long'
|
||||
if short_triggered and not long_triggered:
|
||||
return 'short'
|
||||
|
||||
# 如果两个方向都触发了(在同一根1分钟K线内)
|
||||
if long_triggered and short_triggered:
|
||||
# 根据开盘价判断:
|
||||
# 如果开盘价更接近做空触发价,说明先往下走,先触发做空
|
||||
# 如果开盘价更接近做多触发价,说明先往上走,先触发做多
|
||||
dist_to_long = abs(long_trigger - open_price)
|
||||
dist_to_short = abs(short_trigger - open_price)
|
||||
|
||||
if dist_to_short < dist_to_long:
|
||||
return 'short'
|
||||
else:
|
||||
return 'long'
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def check_reverse_signal_in_first_minute(
|
||||
bars_1m: List[Dict],
|
||||
long_trigger: float,
|
||||
short_trigger: float,
|
||||
current_direction: str
|
||||
) -> bool:
|
||||
"""
|
||||
检查反手信号是否在第一分钟触发
|
||||
|
||||
:param bars_1m: 3根1分钟K线数据
|
||||
:param long_trigger: 做多触发价格
|
||||
:param short_trigger: 做空触发价格
|
||||
:param current_direction: 当前持仓方向 ('long' 或 'short')
|
||||
:return: True 表示反手信号在第一分钟触发,False 表示不是
|
||||
"""
|
||||
if not bars_1m:
|
||||
return False
|
||||
|
||||
# 只检查第一分钟K线
|
||||
first_bar = bars_1m[0]
|
||||
high = float(first_bar['high'])
|
||||
low = float(first_bar['low'])
|
||||
|
||||
# 如果当前是多仓,检查空信号是否在第一分钟触发
|
||||
if current_direction == 'long':
|
||||
return low <= short_trigger
|
||||
|
||||
# 如果当前是空仓,检查多信号是否在第一分钟触发
|
||||
if current_direction == 'short':
|
||||
return high >= long_trigger
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def get_body_percent(candle) -> float:
|
||||
"""
|
||||
计算K线实体占价格的百分比
|
||||
:param candle: K线数据
|
||||
:return: 实体百分比(如0.1表示0.1%)
|
||||
"""
|
||||
body = abs(float(candle['open']) - float(candle['close']))
|
||||
price = (float(candle['open']) + float(candle['close'])) / 2
|
||||
if price == 0:
|
||||
return 0
|
||||
return (body / price) * 100
|
||||
|
||||
|
||||
def check_breakout_reverse_signal(
|
||||
all_data_3m: List[Dict],
|
||||
current_idx: int,
|
||||
current_position_direction: str,
|
||||
min_body_percent: float = 0.1
|
||||
) -> tuple:
|
||||
"""
|
||||
检查"突破上一根K线高低点"的反手信号
|
||||
|
||||
规则:
|
||||
- 持多单时:当前K线跌破上一根K线最低点 → 反手开空
|
||||
条件:上一根K线是阴线且实体>0.1%波动
|
||||
- 持空单时:当前K线涨破上一根K线最高点 → 反手开多
|
||||
条件:上一根K线是阳线且实体>0.1%波动
|
||||
|
||||
:param all_data_3m: 3分钟K线数据
|
||||
:param current_idx: 当前K线索引
|
||||
:param current_position_direction: 当前持仓方向 ('long' 或 'short')
|
||||
:param min_body_percent: 最小实体百分比(默认0.1%)
|
||||
:return: (方向, 触发价格, 信号类型) 或 (None, None, None)
|
||||
"""
|
||||
if current_idx <= 0 or current_position_direction is None:
|
||||
return None, None, None
|
||||
|
||||
curr = all_data_3m[current_idx]
|
||||
prev = all_data_3m[current_idx - 1]
|
||||
|
||||
c_high = float(curr['high'])
|
||||
c_low = float(curr['low'])
|
||||
prev_high = float(prev['high'])
|
||||
prev_low = float(prev['low'])
|
||||
|
||||
# 计算上一根K线的实体百分比
|
||||
body_percent = get_body_percent(prev)
|
||||
|
||||
# 持多单时:检查是否跌破上一根K线最低点
|
||||
if current_position_direction == 'long':
|
||||
# 条件:上一根K线是阴线且实体>min_body_percent%
|
||||
if is_bearish(prev) and body_percent >= min_body_percent:
|
||||
if c_low < prev_low:
|
||||
# 触发反手开空信号
|
||||
logger.debug(f"突破反手信号:持多单,当前K线低点{c_low:.2f}跌破上一根阴线低点{prev_low:.2f},实体{body_percent:.3f}%")
|
||||
return 'short', prev_low, 'breakout'
|
||||
|
||||
# 持空单时:检查是否涨破上一根K线最高点
|
||||
elif current_position_direction == 'short':
|
||||
# 条件:上一根K线是阳线且实体>min_body_percent%
|
||||
if is_bullish(prev) and body_percent >= min_body_percent:
|
||||
if c_high > prev_high:
|
||||
# 触发反手开多信号
|
||||
logger.debug(f"突破反手信号:持空单,当前K线高点{c_high:.2f}涨破上一根阳线高点{prev_high:.2f},实体{body_percent:.3f}%")
|
||||
return 'long', prev_high, 'breakout'
|
||||
|
||||
return None, None, None
|
||||
|
||||
|
||||
def check_trigger_with_1m(
|
||||
all_data_3m: List[Dict],
|
||||
current_idx: int,
|
||||
min_body_size: float = 0.1,
|
||||
current_position_direction: str = None,
|
||||
first_minute_only: bool = True
|
||||
) -> tuple:
|
||||
"""
|
||||
检查当前3分钟K线是否触发了交易信号
|
||||
如果同时触发两个方向,使用1分钟K线精确判断顺序
|
||||
|
||||
新增逻辑:如果有持仓且 first_minute_only=True,反手信号必须在第一分钟触发才有效
|
||||
|
||||
:param all_data_3m: 3分钟K线数据
|
||||
:param current_idx: 当前K线索引
|
||||
:param min_body_size: 最小实体大小
|
||||
:param current_position_direction: 当前持仓方向 ('long', 'short', 或 None)
|
||||
:param first_minute_only: 是否只在第一分钟触发反手信号才有效
|
||||
|
||||
返回:(方向, 触发价格, 有效前一根K线索引, 1分钟数据是否使用)
|
||||
"""
|
||||
if current_idx <= 0:
|
||||
return None, None, None, False
|
||||
|
||||
curr = all_data_3m[current_idx]
|
||||
|
||||
# 查找实体>=min_body_size的前一根K线
|
||||
valid_prev_idx, prev = find_valid_prev_bar(all_data_3m, current_idx, min_body_size)
|
||||
|
||||
if prev is None:
|
||||
return None, None, None, False
|
||||
|
||||
long_trigger, short_trigger = get_one_fifth_levels(prev)
|
||||
|
||||
if long_trigger is None:
|
||||
return None, None, None, False
|
||||
|
||||
c_high = float(curr['high'])
|
||||
c_low = float(curr['low'])
|
||||
|
||||
# 检测是否触发
|
||||
long_triggered = c_high >= long_trigger
|
||||
short_triggered = c_low <= short_trigger
|
||||
|
||||
# 如果两个方向都触发,使用1分钟K线精确判断
|
||||
if long_triggered and short_triggered:
|
||||
bars_1m = get_1m_data_for_3m_bar(curr)
|
||||
|
||||
if bars_1m:
|
||||
direction = determine_trigger_order_by_1m(bars_1m, long_trigger, short_trigger)
|
||||
if direction:
|
||||
trigger_price = long_trigger if direction == 'long' else short_trigger
|
||||
|
||||
# 检查反手信号是否需要在第一分钟触发
|
||||
if first_minute_only and current_position_direction and direction != current_position_direction:
|
||||
# 这是一个反手信号,检查是否在第一分钟触发
|
||||
if not check_reverse_signal_in_first_minute(bars_1m, long_trigger, short_trigger, current_position_direction):
|
||||
# 反手信号不是在第一分钟触发,忽略
|
||||
logger.debug(f"反手信号 {direction} 不在第一分钟触发,忽略")
|
||||
return None, None, None, False
|
||||
|
||||
return direction, trigger_price, valid_prev_idx, True
|
||||
|
||||
# 如果没有1分钟数据,使用开盘价距离判断
|
||||
c_open = float(curr['open'])
|
||||
dist_to_long = abs(long_trigger - c_open)
|
||||
dist_to_short = abs(short_trigger - c_open)
|
||||
if dist_to_short <= dist_to_long:
|
||||
return 'short', short_trigger, valid_prev_idx, False
|
||||
else:
|
||||
return 'long', long_trigger, valid_prev_idx, False
|
||||
|
||||
if short_triggered:
|
||||
# 检查是否是反手信号且需要第一分钟触发
|
||||
if first_minute_only and current_position_direction == 'long':
|
||||
bars_1m = get_1m_data_for_3m_bar(curr)
|
||||
if bars_1m and not check_reverse_signal_in_first_minute(bars_1m, long_trigger, short_trigger, 'long'):
|
||||
logger.debug(f"空信号不在第一分钟触发,忽略(当前持多仓)")
|
||||
return None, None, None, False
|
||||
return 'short', short_trigger, valid_prev_idx, False
|
||||
|
||||
if long_triggered:
|
||||
# 检查是否是反手信号且需要第一分钟触发
|
||||
if first_minute_only and current_position_direction == 'short':
|
||||
bars_1m = get_1m_data_for_3m_bar(curr)
|
||||
if bars_1m and not check_reverse_signal_in_first_minute(bars_1m, long_trigger, short_trigger, 'short'):
|
||||
logger.debug(f"多信号不在第一分钟触发,忽略(当前持空仓)")
|
||||
return None, None, None, False
|
||||
return 'long', long_trigger, valid_prev_idx, False
|
||||
|
||||
return None, None, None, False
|
||||
|
||||
|
||||
# ========================= 回测逻辑 =========================
|
||||
|
||||
def backtest_one_third_strategy(
|
||||
dates: List[str],
|
||||
min_body_size: float = 0.1,
|
||||
first_minute_only: bool = True,
|
||||
enable_breakout_reverse: bool = True,
|
||||
breakout_min_body_percent: float = 0.1
|
||||
):
|
||||
"""
|
||||
三分之一策略回测(精准版)
|
||||
|
||||
:param dates: 日期列表
|
||||
:param min_body_size: 最小实体大小(绝对值)
|
||||
:param first_minute_only: 是否只在第一分钟触发反手信号才有效(默认True)
|
||||
:param enable_breakout_reverse: 是否启用"突破上一根K线高低点"反手信号(默认True)
|
||||
:param breakout_min_body_percent: 突破反手信号的最小实体百分比(默认0.1%)
|
||||
:return: (trades, stats)
|
||||
"""
|
||||
# 获取所有3分钟K线数据
|
||||
all_data: List[Dict] = []
|
||||
total_queried = 0
|
||||
for d in dates:
|
||||
day_data = get_3m_data_by_date(d)
|
||||
all_data.extend(day_data)
|
||||
if day_data:
|
||||
total_queried += len(day_data)
|
||||
|
||||
logger.info(f"总共查询了 {len(dates)} 天,获取到 {total_queried} 条3分钟K线数据")
|
||||
logger.info(f"反手信号仅第一分钟有效: {first_minute_only}")
|
||||
logger.info(f"突破反手信号启用: {enable_breakout_reverse},最小实体百分比: {breakout_min_body_percent}%")
|
||||
|
||||
if not all_data:
|
||||
logger.warning("未获取到任何数据,请检查数据库")
|
||||
return [], {'long': {'count': 0, 'wins': 0, 'total_profit': 0.0},
|
||||
'short': {'count': 0, 'wins': 0, 'total_profit': 0.0}}
|
||||
|
||||
# 按时间戳排序
|
||||
all_data.sort(key=lambda x: x['id'])
|
||||
|
||||
# 验证排序结果
|
||||
if len(all_data) > 1:
|
||||
first_ts = all_data[0]['id']
|
||||
last_ts = all_data[-1]['id']
|
||||
first_time = datetime.datetime.fromtimestamp(first_ts / 1000)
|
||||
last_time = datetime.datetime.fromtimestamp(last_ts / 1000)
|
||||
logger.info(f"数据范围:{first_time.strftime('%Y-%m-%d %H:%M:%S')} 到 {last_time.strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
|
||||
stats = {
|
||||
'long': {'count': 0, 'wins': 0, 'total_profit': 0.0, 'name': '做多'},
|
||||
'short': {'count': 0, 'wins': 0, 'total_profit': 0.0, 'name': '做空'},
|
||||
}
|
||||
|
||||
trades: List[Dict] = []
|
||||
current_position: Optional[Dict] = None
|
||||
|
||||
# 统计使用1分钟数据精准判断的次数
|
||||
precise_count = 0
|
||||
fallback_count = 0
|
||||
# 统计突破反手信号触发次数
|
||||
breakout_reverse_count = 0
|
||||
# 记录每根K线是否已经执行过突破反手(当前K线只执行一次)
|
||||
last_breakout_bar_id = None
|
||||
|
||||
idx = 1
|
||||
while idx < len(all_data):
|
||||
curr = all_data[idx]
|
||||
curr_bar_id = curr['id']
|
||||
|
||||
# 获取当前持仓方向(用于判断反手信号是否在第一分钟触发)
|
||||
current_pos_dir = current_position['direction'] if current_position else None
|
||||
|
||||
# 检测信号(使用1分钟K线精准判断,并考虑反手信号第一分钟限制)
|
||||
direction, trigger_price, valid_prev_idx, used_1m = check_trigger_with_1m(
|
||||
all_data, idx, min_body_size,
|
||||
current_position_direction=current_pos_dir,
|
||||
first_minute_only=first_minute_only
|
||||
)
|
||||
|
||||
# 如果没有五分之一信号,且有持仓,检查突破反手信号
|
||||
signal_type = 'one_fifth' # 信号类型:one_fifth(五分之一)或 breakout(突破)
|
||||
if direction is None and current_position is not None and enable_breakout_reverse:
|
||||
# 检查当前K线是否已经执行过突破反手
|
||||
if last_breakout_bar_id != curr_bar_id:
|
||||
breakout_dir, breakout_price, breakout_type = check_breakout_reverse_signal(
|
||||
all_data, idx, current_pos_dir, breakout_min_body_percent
|
||||
)
|
||||
if breakout_dir:
|
||||
direction = breakout_dir
|
||||
trigger_price = breakout_price
|
||||
signal_type = 'breakout'
|
||||
|
||||
if used_1m:
|
||||
precise_count += 1
|
||||
elif direction and signal_type == 'one_fifth':
|
||||
fallback_count += 1
|
||||
|
||||
# 无持仓 -> 开仓(只用五分之一信号开仓,突破信号不用于开仓)
|
||||
if current_position is None:
|
||||
if direction and signal_type == 'one_fifth':
|
||||
current_position = {
|
||||
'direction': direction,
|
||||
'entry_price': trigger_price,
|
||||
'entry_time': curr['id'],
|
||||
'entry_bar_idx': idx
|
||||
}
|
||||
stats[direction]['count'] += 1
|
||||
|
||||
time_str = datetime.datetime.fromtimestamp(curr['id'] / 1000).strftime('%Y-%m-%d %H:%M')
|
||||
logger.debug(f"[{time_str}] 开仓{direction} @ {trigger_price:.2f}")
|
||||
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
# 有持仓 -> 检查是否需要反向
|
||||
pos_dir = current_position['direction']
|
||||
|
||||
if direction and direction != pos_dir:
|
||||
# 反向信号,平仓并反手
|
||||
exit_price = trigger_price
|
||||
|
||||
if pos_dir == 'long':
|
||||
diff = exit_price - current_position['entry_price']
|
||||
else:
|
||||
diff = current_position['entry_price'] - exit_price
|
||||
|
||||
# 记录信号类型
|
||||
signal_type_str = '突破' if signal_type == 'breakout' else '五分之一'
|
||||
|
||||
trades.append({
|
||||
'entry_time': datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
'exit_time': datetime.datetime.fromtimestamp(curr['id'] / 1000),
|
||||
'direction': '做多' if pos_dir == 'long' else '做空',
|
||||
'entry': current_position['entry_price'],
|
||||
'exit': exit_price,
|
||||
'diff': diff,
|
||||
'signal_type': signal_type_str
|
||||
})
|
||||
|
||||
stats[pos_dir]['total_profit'] += diff
|
||||
if diff > 0:
|
||||
stats[pos_dir]['wins'] += 1
|
||||
|
||||
# 如果是突破反手信号,记录当前K线ID,防止重复执行
|
||||
if signal_type == 'breakout':
|
||||
last_breakout_bar_id = curr_bar_id
|
||||
breakout_reverse_count += 1
|
||||
|
||||
# 反手开仓
|
||||
current_position = {
|
||||
'direction': direction,
|
||||
'entry_price': trigger_price,
|
||||
'entry_time': curr['id'],
|
||||
'entry_bar_idx': idx
|
||||
}
|
||||
stats[direction]['count'] += 1
|
||||
|
||||
time_str = datetime.datetime.fromtimestamp(curr['id'] / 1000).strftime('%Y-%m-%d %H:%M')
|
||||
logger.debug(f"[{time_str}] 平{pos_dir}反手{direction}({signal_type_str}) @ {trigger_price:.2f} 盈亏={diff:.2f}")
|
||||
|
||||
idx += 1
|
||||
|
||||
# 尾仓处理:最后一根K线收盘价平仓
|
||||
if current_position:
|
||||
last = all_data[-1]
|
||||
exit_price = float(last['close'])
|
||||
pos_dir = current_position['direction']
|
||||
|
||||
if pos_dir == 'long':
|
||||
diff = exit_price - current_position['entry_price']
|
||||
else:
|
||||
diff = current_position['entry_price'] - exit_price
|
||||
|
||||
trades.append({
|
||||
'entry_time': datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
'exit_time': datetime.datetime.fromtimestamp(last['id'] / 1000),
|
||||
'direction': '做多' if pos_dir == 'long' else '做空',
|
||||
'entry': current_position['entry_price'],
|
||||
'exit': exit_price,
|
||||
'diff': diff
|
||||
})
|
||||
|
||||
stats[pos_dir]['total_profit'] += diff
|
||||
if diff > 0:
|
||||
stats[pos_dir]['wins'] += 1
|
||||
|
||||
logger.info(f"回测完成:使用1分钟精准判断 {precise_count} 次,使用开盘价距离判断 {fallback_count} 次,突破反手信号 {breakout_reverse_count} 次")
|
||||
|
||||
return trades, stats
|
||||
|
||||
|
||||
# ========================= 运行回测 =========================
|
||||
|
||||
if __name__ == '__main__':
|
||||
# ==================== 配置参数 ====================
|
||||
START_DATE = "2025-01-01"
|
||||
END_DATE = "2025-12-31"
|
||||
MIN_BODY_SIZE = 0.1 # 最小实体大小(绝对值)
|
||||
FIRST_MINUTE_ONLY = True # 反手信号仅在3分钟K线的第一分钟触发才有效
|
||||
|
||||
# 突破反手信号配置
|
||||
ENABLE_BREAKOUT_REVERSE = True # 是否启用"突破上一根K线高低点"反手信号
|
||||
BREAKOUT_MIN_BODY_PERCENT = 0.1 # 突破反手信号的最小实体百分比(0.1表示0.1%)
|
||||
|
||||
# ==================== 生成查询日期列表 ====================
|
||||
dates = []
|
||||
|
||||
try:
|
||||
start_dt = datetime.datetime.strptime(START_DATE, '%Y-%m-%d')
|
||||
end_dt = datetime.datetime.strptime(END_DATE, '%Y-%m-%d')
|
||||
|
||||
if start_dt > end_dt:
|
||||
logger.error(f"开始日期 {START_DATE} 不能晚于结束日期 {END_DATE}")
|
||||
exit(1)
|
||||
|
||||
current_dt = start_dt
|
||||
while current_dt <= end_dt:
|
||||
dates.append(current_dt.strftime('%Y-%m-%d'))
|
||||
current_dt += datetime.timedelta(days=1)
|
||||
|
||||
logger.info(f"回测日期范围:{START_DATE} 到 {END_DATE},共 {len(dates)} 天")
|
||||
except ValueError as e:
|
||||
logger.error(f"日期格式错误:{e}")
|
||||
exit(1)
|
||||
|
||||
# ==================== 执行回测 ====================
|
||||
trades, stats = backtest_one_third_strategy(
|
||||
dates,
|
||||
MIN_BODY_SIZE,
|
||||
FIRST_MINUTE_ONLY,
|
||||
ENABLE_BREAKOUT_REVERSE,
|
||||
BREAKOUT_MIN_BODY_PERCENT
|
||||
)
|
||||
|
||||
# ==================== 输出交易详情 ====================
|
||||
logger.info("===== 每笔交易详情 =====")
|
||||
|
||||
# 参数设定
|
||||
contract_size = 10000 # 合约规模
|
||||
open_fee_fixed = 5 # 固定开仓手续费
|
||||
close_fee_rate = 0.0005 # 平仓手续费率
|
||||
|
||||
total_points_profit = 0
|
||||
total_money_profit = 0
|
||||
total_fee = 0
|
||||
|
||||
for t in trades:
|
||||
entry = t['entry']
|
||||
exit_price = t['exit']
|
||||
direction = t['direction']
|
||||
|
||||
# 原始价差
|
||||
point_diff = t['diff']
|
||||
|
||||
# 金额盈利
|
||||
money_profit = point_diff / entry * contract_size
|
||||
|
||||
# 手续费
|
||||
fee = open_fee_fixed + (contract_size / entry * exit_price * close_fee_rate)
|
||||
|
||||
# 净利润
|
||||
net_profit = money_profit - fee
|
||||
|
||||
t.update({
|
||||
'point_diff': point_diff,
|
||||
'raw_profit': money_profit,
|
||||
'fee': fee,
|
||||
'net_profit': net_profit
|
||||
})
|
||||
|
||||
total_points_profit += point_diff
|
||||
total_money_profit += money_profit
|
||||
total_fee += fee
|
||||
|
||||
logger.info(
|
||||
f"{t['entry_time']} {direction} "
|
||||
f"入={entry:.2f} 出={exit_price:.2f} 差价={point_diff:.2f} "
|
||||
f"原始盈利={money_profit:.2f} 手续费={fee:.2f} 净利润={net_profit:.2f} {t['exit_time']}"
|
||||
)
|
||||
|
||||
# ==================== 汇总统计 ====================
|
||||
total_net_profit = total_money_profit - total_fee
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print(f"【BitMart 五分之一策略回测结果(3分钟K线 + 1分钟精准判断)】")
|
||||
print(f"{'='*60}")
|
||||
print(f"回测周期:{START_DATE} 到 {END_DATE}")
|
||||
print(f"最小实体要求:{MIN_BODY_SIZE}")
|
||||
print(f"反手信号仅第一分钟有效:{'是' if FIRST_MINUTE_ONLY else '否'}")
|
||||
print(f"突破反手信号:{'启用' if ENABLE_BREAKOUT_REVERSE else '禁用'}(最小实体{BREAKOUT_MIN_BODY_PERCENT}%)")
|
||||
print(f"{'='*60}")
|
||||
print(f"总交易笔数:{len(trades)}")
|
||||
print(f"总点差:{total_points_profit:.2f}")
|
||||
print(f"总原始盈利(未扣费):{total_money_profit:.2f}")
|
||||
print(f"总手续费:{total_fee:.2f}")
|
||||
print(f"总净利润:{total_net_profit:.2f}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
print("\n===== 方向统计 =====")
|
||||
for k, v in stats.items():
|
||||
name = v['name']
|
||||
count = v['count']
|
||||
wins = v['wins']
|
||||
total_p = v['total_profit']
|
||||
win_rate = (wins / count * 100) if count > 0 else 0.0
|
||||
avg_p = (total_p / count) if count > 0 else 0.0
|
||||
print(f"{name}: 次数={count} 胜率={win_rate:.2f}% 总价差={total_p:.2f} 平均价差={avg_p:.2f}")
|
||||
|
||||
# 保存交易记录到CSV
|
||||
if trades:
|
||||
import csv
|
||||
csv_path = Path(__file__).parent / 'backtest_one_third_trades.csv'
|
||||
with open(csv_path, 'w', newline='', encoding='utf-8') as f:
|
||||
writer = csv.DictWriter(f, fieldnames=[
|
||||
'entry_time', 'exit_time', 'direction', 'entry', 'exit',
|
||||
'point_diff', 'raw_profit', 'fee', 'net_profit', 'signal_type'
|
||||
])
|
||||
writer.writeheader()
|
||||
for t in trades:
|
||||
writer.writerow({
|
||||
'entry_time': t['entry_time'],
|
||||
'exit_time': t['exit_time'],
|
||||
'direction': t['direction'],
|
||||
'entry': t['entry'],
|
||||
'exit': t['exit'],
|
||||
'point_diff': t['point_diff'],
|
||||
'raw_profit': t['raw_profit'],
|
||||
'fee': t['fee'],
|
||||
'net_profit': t['net_profit'],
|
||||
'signal_type': t.get('signal_type', '五分之一')
|
||||
})
|
||||
print(f"\n交易记录已保存到:{csv_path}")
|
||||
@@ -1,764 +0,0 @@
|
||||
"""
|
||||
量化交易回测系统 - 三分之一回归策略(双向触发版)
|
||||
|
||||
========== 策略规则 ==========
|
||||
|
||||
1. 触发价格计算(基于有效的前一根K线,实体>=0.1):
|
||||
- 做多触发价格 = 收盘价 + 实体/3(从收盘价往上涨1/3)
|
||||
- 做空触发价格 = 收盘价 - 实体/3(从收盘价往下跌1/3)
|
||||
|
||||
2. 信号触发条件:
|
||||
- 当前K线最高价 >= 做多触发价格 → 做多信号
|
||||
- 当前K线最低价 <= 做空触发价格 → 做空信号
|
||||
|
||||
3. 执行逻辑:
|
||||
- 做多时遇到做空信号 -> 平多并反手开空
|
||||
- 做空时遇到做多信号 -> 平空并反手开多
|
||||
- 同一根K线内只交易一次,防止频繁反手
|
||||
|
||||
4. 实体过滤:
|
||||
- 如果前一根K线的实体部分(|open - close|)< 0.1,继续往前查找
|
||||
- 直到找到实体>=0.1的K线,再用那根K线来计算触发价格
|
||||
|
||||
示例1(阳线):
|
||||
前一根K线:开盘3000,收盘3100(阳线,实体=100)
|
||||
- 做多触发价格 = 3100 + 33 = 3133(继续上涨做多)
|
||||
- 做空触发价格 = 3100 - 33 = 3067(回调做空)
|
||||
|
||||
示例2(阴线):
|
||||
前一根K线:开盘3100,收盘3000(阴线,实体=100)
|
||||
- 做多触发价格 = 3000 + 33 = 3033(反弹做多)
|
||||
- 做空触发价格 = 3000 - 33 = 2967(继续下跌做空)
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import calendar
|
||||
import os
|
||||
from typing import List, Dict, Optional
|
||||
from loguru import logger
|
||||
import pandas as pd
|
||||
import mplfinance as mpf
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib
|
||||
try:
|
||||
import plotly.graph_objects as go
|
||||
except Exception:
|
||||
go = None
|
||||
from models.bitmart_klines import BitMartETH5M
|
||||
|
||||
# 配置中文字体
|
||||
import matplotlib.font_manager as fm
|
||||
import warnings
|
||||
|
||||
# 忽略matplotlib的字体警告
|
||||
warnings.filterwarnings('ignore', category=UserWarning, module='matplotlib.font_manager')
|
||||
warnings.filterwarnings('ignore', message='.*Glyph.*missing.*', category=UserWarning)
|
||||
|
||||
# 尝试设置中文字体,按优先级尝试
|
||||
chinese_fonts = ['SimHei', 'Microsoft YaHei', 'SimSun', 'KaiTi', 'FangSong', 'STSong', 'STHeiti']
|
||||
available_fonts = [f.name for f in fm.fontManager.ttflist]
|
||||
|
||||
# 找到第一个可用的中文字体
|
||||
font_found = None
|
||||
for font_name in chinese_fonts:
|
||||
if font_name in available_fonts:
|
||||
font_found = font_name
|
||||
break
|
||||
|
||||
if font_found:
|
||||
plt.rcParams['font.sans-serif'] = [font_found] + ['DejaVu Sans']
|
||||
logger.info(f"使用中文字体: {font_found}")
|
||||
else:
|
||||
# 如果没有找到中文字体,尝试使用系统默认字体
|
||||
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'SimSun', 'Arial Unicode MS', 'DejaVu Sans']
|
||||
logger.warning("未找到中文字体,使用默认配置")
|
||||
|
||||
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
|
||||
plt.rcParams['font.size'] = 10 # 设置默认字体大小
|
||||
|
||||
# 尝试清除字体缓存(如果可能)
|
||||
try:
|
||||
# 不强制重建,避免性能问题
|
||||
pass
|
||||
except:
|
||||
pass
|
||||
|
||||
# 获取当前脚本所在目录
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# ========================= 工具函数 =========================
|
||||
|
||||
def is_bullish(c): # 阳线
|
||||
return float(c['close']) > float(c['open'])
|
||||
|
||||
|
||||
def is_bearish(c): # 阴线
|
||||
return float(c['close']) < float(c['open'])
|
||||
|
||||
|
||||
def get_body_size(candle):
|
||||
"""计算K线实体大小(绝对值)"""
|
||||
return abs(float(candle['open']) - float(candle['close']))
|
||||
|
||||
|
||||
def find_valid_prev_bar(all_data: List[Dict], current_idx: int, min_body_size: float = 0.1):
|
||||
"""
|
||||
从当前索引往前查找,直到找到实体>=min_body_size的K线
|
||||
返回:(有效K线的索引, K线数据) 或 (None, None)
|
||||
"""
|
||||
if current_idx <= 0:
|
||||
return None, None
|
||||
|
||||
for i in range(current_idx - 1, -1, -1):
|
||||
prev = all_data[i]
|
||||
body_size = get_body_size(prev)
|
||||
if body_size >= min_body_size:
|
||||
return i, prev
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
def get_one_third_levels(prev):
|
||||
"""
|
||||
计算前一根K线实体的 1/3 双向触发价格
|
||||
返回:(做多触发价格, 做空触发价格)
|
||||
|
||||
基于收盘价计算(无论阴线阳线):
|
||||
- 做多触发价格 = 收盘价 + 实体/3(从收盘价往上涨1/3实体)
|
||||
- 做空触发价格 = 收盘价 - 实体/3(从收盘价往下跌1/3实体)
|
||||
|
||||
示例:
|
||||
阳线 open=3000, close=3100, 实体=100
|
||||
- 做多触发 = 3100 + 33 = 3133(继续涨)
|
||||
- 做空触发 = 3100 - 33 = 3067(回调)
|
||||
|
||||
阴线 open=3100, close=3000, 实体=100
|
||||
- 做多触发 = 3000 + 33 = 3033(反弹)
|
||||
- 做空触发 = 3000 - 33 = 2967(继续跌)
|
||||
"""
|
||||
p_open = float(prev['open'])
|
||||
p_close = float(prev['close'])
|
||||
|
||||
body = abs(p_open - p_close)
|
||||
|
||||
if body < 0.001: # 十字星,忽略
|
||||
return None, None
|
||||
|
||||
# 基于收盘价的双向触发价格
|
||||
long_trigger = p_close + body / 3 # 从收盘价往上涨1/3触发做多
|
||||
short_trigger = p_close - body / 3 # 从收盘价往下跌1/3触发做空
|
||||
|
||||
return long_trigger, short_trigger
|
||||
|
||||
|
||||
def check_trigger(all_data: List[Dict], current_idx: int, min_body_size: float = 0.1):
|
||||
"""
|
||||
检查当前K线是否触发了交易信号(双向检测)
|
||||
返回:(方向, 触发价格, 有效前一根K线索引) 或 (None, None, None)
|
||||
|
||||
规则:
|
||||
- 当前K线高点 >= 做多触发价格 → 做多信号
|
||||
- 当前K线低点 <= 做空触发价格 → 做空信号
|
||||
- 如果同时触发两个方向,以先触发的为准(这里简化为优先做空,因为下跌更快)
|
||||
"""
|
||||
if current_idx <= 0:
|
||||
return None, None, None
|
||||
|
||||
curr = all_data[current_idx]
|
||||
|
||||
# 查找实体>=min_body_size的前一根K线
|
||||
valid_prev_idx, prev = find_valid_prev_bar(all_data, current_idx, min_body_size)
|
||||
|
||||
if prev is None:
|
||||
return None, None, None
|
||||
|
||||
long_trigger, short_trigger = get_one_third_levels(prev)
|
||||
|
||||
if long_trigger is None:
|
||||
return None, None, None
|
||||
|
||||
# 使用影线部分(high/low)来判断
|
||||
c_high = float(curr['high'])
|
||||
c_low = float(curr['low'])
|
||||
|
||||
# 检测是否触发
|
||||
long_triggered = c_high >= long_trigger
|
||||
short_triggered = c_low <= short_trigger
|
||||
|
||||
# 如果两个方向都触发,需要判断哪个先触发
|
||||
# 简化处理:比较触发价格距离开盘价的远近,更近的先触发
|
||||
if long_triggered and short_triggered:
|
||||
c_open = float(curr['open'])
|
||||
dist_to_long = abs(long_trigger - c_open)
|
||||
dist_to_short = abs(short_trigger - c_open)
|
||||
if dist_to_short <= dist_to_long:
|
||||
return 'short', short_trigger, valid_prev_idx
|
||||
else:
|
||||
return 'long', long_trigger, valid_prev_idx
|
||||
|
||||
if short_triggered:
|
||||
return 'short', short_trigger, valid_prev_idx
|
||||
|
||||
if long_triggered:
|
||||
return 'long', long_trigger, valid_prev_idx
|
||||
|
||||
return None, None, None
|
||||
|
||||
|
||||
def get_data_by_date(model, date_str: str):
|
||||
"""按天获取指定表的数据"""
|
||||
try:
|
||||
target_date = datetime.datetime.strptime(date_str, '%Y-%m-%d')
|
||||
except ValueError:
|
||||
logger.error("日期格式不正确,请使用 YYYY-MM-DD 格式。")
|
||||
return []
|
||||
|
||||
start_ts = int(target_date.timestamp() * 1000)
|
||||
end_ts = int((target_date + datetime.timedelta(days=1)).timestamp() * 1000) - 1
|
||||
|
||||
query = model.select().where(model.id.between(start_ts, end_ts)).order_by(model.id.asc())
|
||||
data = [{'id': i.id, 'open': i.open, 'high': i.high, 'low': i.low, 'close': i.close} for i in query]
|
||||
|
||||
if data:
|
||||
data.sort(key=lambda x: x['id'])
|
||||
|
||||
return data
|
||||
|
||||
|
||||
# ========================= 回测逻辑 =========================
|
||||
|
||||
def backtest_one_third_strategy(dates: List[str]):
|
||||
"""三分之一回归策略回测(优化版)"""
|
||||
all_data: List[Dict] = []
|
||||
|
||||
for d in dates:
|
||||
day_data = get_data_by_date(BitMartETH5M, d)
|
||||
all_data.extend(day_data)
|
||||
|
||||
logger.info(f"总共查询了 {len(dates)} 天,获取到 {len(all_data)} 条K线数据")
|
||||
|
||||
if not all_data:
|
||||
logger.warning("未获取到任何数据")
|
||||
return [], {'long': {'count': 0, 'wins': 0, 'total_profit': 0.0},
|
||||
'short': {'count': 0, 'wins': 0, 'total_profit': 0.0}}
|
||||
|
||||
all_data.sort(key=lambda x: x['id'])
|
||||
|
||||
if len(all_data) > 1:
|
||||
first_time = datetime.datetime.fromtimestamp(all_data[0]['id'] / 1000)
|
||||
last_time = datetime.datetime.fromtimestamp(all_data[-1]['id'] / 1000)
|
||||
logger.info(f"数据范围:{first_time.strftime('%Y-%m-%d %H:%M')} 到 {last_time.strftime('%Y-%m-%d %H:%M')}")
|
||||
|
||||
# 验证排序:打印前5条数据
|
||||
logger.info("===== 前5条数据(验证排序)=====")
|
||||
for i in range(min(5, len(all_data))):
|
||||
d = all_data[i]
|
||||
t = datetime.datetime.fromtimestamp(d['id'] / 1000).strftime('%Y-%m-%d %H:%M:%S')
|
||||
k_type = "阳线" if is_bullish(d) else ("阴线" if is_bearish(d) else "十字星")
|
||||
logger.info(f" [{i}] {t} | {k_type} | O={d['open']:.2f} H={d['high']:.2f} L={d['low']:.2f} C={d['close']:.2f}")
|
||||
|
||||
stats = {
|
||||
'long': {'count': 0, 'wins': 0, 'total_profit': 0.0, 'name': '做多'},
|
||||
'short': {'count': 0, 'wins': 0, 'total_profit': 0.0, 'name': '做空'},
|
||||
}
|
||||
|
||||
# 额外统计信息
|
||||
extra_stats = {
|
||||
'same_dir_ignored': 0, # 同向信号被忽略次数
|
||||
'no_signal_bars': 0, # 无信号K线数
|
||||
'total_bars': len(all_data) - 1, # 总K线数(排除第一根)
|
||||
}
|
||||
|
||||
trades: List[Dict] = []
|
||||
current_position: Optional[Dict] = None
|
||||
last_trade_bar: Optional[int] = None # 记录上次交易的K线索引,防止同一K线重复交易
|
||||
|
||||
for idx in range(1, len(all_data)):
|
||||
curr = all_data[idx]
|
||||
|
||||
# 使用check_trigger函数,它会自动查找实体>=0.1的前一根K线
|
||||
direction, trigger_price, valid_prev_idx = check_trigger(all_data, idx, min_body_size=0.1)
|
||||
|
||||
# 获取有效的前一根K线用于日志输出
|
||||
valid_prev = all_data[valid_prev_idx] if valid_prev_idx is not None else None
|
||||
|
||||
# 无信号时跳过
|
||||
if direction is None:
|
||||
extra_stats['no_signal_bars'] += 1
|
||||
continue
|
||||
|
||||
# 同一K线内已交易,跳过(与交易代码逻辑一致)
|
||||
if last_trade_bar == idx:
|
||||
continue
|
||||
|
||||
# 空仓时,有信号就开仓
|
||||
if current_position is None:
|
||||
if valid_prev is not None:
|
||||
# 打印开仓时的K线信息
|
||||
prev_time = datetime.datetime.fromtimestamp(valid_prev['id'] / 1000).strftime('%Y-%m-%d %H:%M')
|
||||
curr_time = datetime.datetime.fromtimestamp(curr['id'] / 1000).strftime('%Y-%m-%d %H:%M')
|
||||
prev_type = "阳线" if is_bullish(valid_prev) else ("阴线" if is_bearish(valid_prev) else "十字星")
|
||||
curr_type = "阳线" if is_bullish(curr) else ("阴线" if is_bearish(curr) else "十字星")
|
||||
prev_body = get_body_size(valid_prev)
|
||||
logger.info(f"【开仓】{direction} @ {trigger_price:.2f}")
|
||||
logger.info(f" 有效前一根[{prev_time}]: {prev_type} 实体={prev_body:.2f} O={valid_prev['open']:.2f} H={valid_prev['high']:.2f} L={valid_prev['low']:.2f} C={valid_prev['close']:.2f}")
|
||||
logger.info(f" 当前根[{curr_time}]: {curr_type} O={curr['open']:.2f} H={curr['high']:.2f} L={curr['low']:.2f} C={curr['close']:.2f}")
|
||||
|
||||
current_position = {
|
||||
'direction': direction,
|
||||
'entry_price': trigger_price,
|
||||
'entry_time': curr['id'],
|
||||
'entry_bar': idx
|
||||
}
|
||||
stats[direction]['count'] += 1
|
||||
last_trade_bar = idx # 记录交易K线
|
||||
continue
|
||||
|
||||
# 有仓位时,检查信号
|
||||
pos_dir = current_position['direction']
|
||||
|
||||
# 同向信号,忽略(与交易代码逻辑一致)
|
||||
if direction == pos_dir:
|
||||
extra_stats['same_dir_ignored'] += 1
|
||||
continue
|
||||
|
||||
# 反向信号,平仓反手
|
||||
if valid_prev is not None:
|
||||
exit_price = trigger_price
|
||||
|
||||
if pos_dir == 'long':
|
||||
diff = exit_price - current_position['entry_price']
|
||||
else:
|
||||
diff = current_position['entry_price'] - exit_price
|
||||
|
||||
# 打印平仓时的K线信息
|
||||
prev_time = datetime.datetime.fromtimestamp(valid_prev['id'] / 1000).strftime('%Y-%m-%d %H:%M')
|
||||
curr_time = datetime.datetime.fromtimestamp(curr['id'] / 1000).strftime('%Y-%m-%d %H:%M')
|
||||
prev_type = "阳线" if is_bullish(valid_prev) else ("阴线" if is_bearish(valid_prev) else "十字星")
|
||||
curr_type = "阳线" if is_bullish(curr) else ("阴线" if is_bearish(curr) else "十字星")
|
||||
prev_body = get_body_size(valid_prev)
|
||||
logger.info(f"【平仓反手】{pos_dir} -> {direction} @ {exit_price:.2f}, 盈亏: {diff:.2f}")
|
||||
logger.info(f" 有效前一根[{prev_time}]: {prev_type} 实体={prev_body:.2f} O={valid_prev['open']:.2f} H={valid_prev['high']:.2f} L={valid_prev['low']:.2f} C={valid_prev['close']:.2f}")
|
||||
logger.info(f" 当前根[{curr_time}]: {curr_type} O={curr['open']:.2f} H={curr['high']:.2f} L={curr['low']:.2f} C={curr['close']:.2f}")
|
||||
|
||||
trades.append({
|
||||
'entry_time': datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
'exit_time': datetime.datetime.fromtimestamp(curr['id'] / 1000),
|
||||
'entry_time_ms': current_position['entry_time'],
|
||||
'exit_time_ms': curr['id'],
|
||||
'direction': '做多' if pos_dir == 'long' else '做空',
|
||||
'entry': current_position['entry_price'],
|
||||
'exit': exit_price,
|
||||
'diff': diff,
|
||||
'hold_bars': idx - current_position['entry_bar'] # 持仓K线数
|
||||
})
|
||||
|
||||
stats[pos_dir]['total_profit'] += diff
|
||||
if diff > 0:
|
||||
stats[pos_dir]['wins'] += 1
|
||||
|
||||
# 反手开仓
|
||||
current_position = {
|
||||
'direction': direction,
|
||||
'entry_price': trigger_price,
|
||||
'entry_time': curr['id'],
|
||||
'entry_bar': idx
|
||||
}
|
||||
stats[direction]['count'] += 1
|
||||
last_trade_bar = idx # 记录交易K线
|
||||
|
||||
# 尾仓处理
|
||||
if current_position:
|
||||
last = all_data[-1]
|
||||
last_idx = len(all_data) - 1
|
||||
exit_price = float(last['close'])
|
||||
pos_dir = current_position['direction']
|
||||
|
||||
if pos_dir == 'long':
|
||||
diff = exit_price - current_position['entry_price']
|
||||
else:
|
||||
diff = current_position['entry_price'] - exit_price
|
||||
|
||||
trades.append({
|
||||
'entry_time': datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
'exit_time': datetime.datetime.fromtimestamp(last['id'] / 1000),
|
||||
'entry_time_ms': current_position['entry_time'],
|
||||
'exit_time_ms': last['id'],
|
||||
'direction': '做多' if pos_dir == 'long' else '做空',
|
||||
'entry': current_position['entry_price'],
|
||||
'exit': exit_price,
|
||||
'diff': diff,
|
||||
'hold_bars': last_idx - current_position['entry_bar'], # 持仓K线数
|
||||
'is_tail': True # 标记为尾仓平仓
|
||||
})
|
||||
stats[pos_dir]['total_profit'] += diff
|
||||
if diff > 0:
|
||||
stats[pos_dir]['wins'] += 1
|
||||
|
||||
logger.info(f"【尾仓平仓】{pos_dir} @ {exit_price:.2f}, 盈亏: {diff:.2f}")
|
||||
|
||||
# 打印额外统计信息
|
||||
logger.info(f"\n===== 信号统计 =====")
|
||||
logger.info(f"总K线数: {extra_stats['total_bars']}")
|
||||
logger.info(f"无信号K线: {extra_stats['no_signal_bars']} ({extra_stats['no_signal_bars']/extra_stats['total_bars']*100:.1f}%)")
|
||||
logger.info(f"同向信号忽略: {extra_stats['same_dir_ignored']}")
|
||||
|
||||
return trades, stats, all_data, extra_stats
|
||||
|
||||
|
||||
# ========================= 绘图函数 =========================
|
||||
def plot_trades(all_data: List[Dict], trades: List[Dict], save_path: str = None):
|
||||
"""
|
||||
绘制K线图并标注交易点位
|
||||
"""
|
||||
if not all_data:
|
||||
logger.warning("没有数据可绘制")
|
||||
return
|
||||
|
||||
# 转换为 DataFrame
|
||||
df = pd.DataFrame(all_data)
|
||||
df['datetime'] = pd.to_datetime(df['id'], unit='ms')
|
||||
df.set_index('datetime', inplace=True)
|
||||
df = df.rename(columns={'open': 'Open', 'high': 'High', 'low': 'Low', 'close': 'Close'})
|
||||
df = df[['Open', 'High', 'Low', 'Close']]
|
||||
|
||||
# 准备标记点
|
||||
buy_signals = [] # 做多开仓
|
||||
sell_signals = [] # 做空开仓
|
||||
buy_exits = [] # 做多平仓
|
||||
sell_exits = [] # 做空平仓
|
||||
|
||||
for trade in trades:
|
||||
entry_time = pd.to_datetime(trade['entry_time_ms'], unit='ms')
|
||||
exit_time = pd.to_datetime(trade['exit_time_ms'], unit='ms')
|
||||
direction = trade['direction']
|
||||
entry_price = trade['entry']
|
||||
exit_price = trade['exit']
|
||||
|
||||
if direction == '做多':
|
||||
buy_signals.append((entry_time, entry_price))
|
||||
buy_exits.append((exit_time, exit_price))
|
||||
else:
|
||||
sell_signals.append((entry_time, entry_price))
|
||||
sell_exits.append((exit_time, exit_price))
|
||||
|
||||
# 创建标记序列
|
||||
buy_markers = pd.Series(index=df.index, dtype=float)
|
||||
sell_markers = pd.Series(index=df.index, dtype=float)
|
||||
buy_exit_markers = pd.Series(index=df.index, dtype=float)
|
||||
sell_exit_markers = pd.Series(index=df.index, dtype=float)
|
||||
|
||||
for t, p in buy_signals:
|
||||
if t in buy_markers.index:
|
||||
buy_markers[t] = p
|
||||
for t, p in sell_signals:
|
||||
if t in sell_markers.index:
|
||||
sell_markers[t] = p
|
||||
for t, p in buy_exits:
|
||||
if t in buy_exit_markers.index:
|
||||
buy_exit_markers[t] = p
|
||||
for t, p in sell_exits:
|
||||
if t in sell_exit_markers.index:
|
||||
sell_exit_markers[t] = p
|
||||
|
||||
# 添加标记
|
||||
add_plots = []
|
||||
|
||||
if buy_markers.notna().any():
|
||||
add_plots.append(mpf.make_addplot(buy_markers, type='scatter', markersize=100,
|
||||
marker='^', color='green', label='做多开仓'))
|
||||
if sell_markers.notna().any():
|
||||
add_plots.append(mpf.make_addplot(sell_markers, type='scatter', markersize=100,
|
||||
marker='v', color='red', label='做空开仓'))
|
||||
if buy_exit_markers.notna().any():
|
||||
add_plots.append(mpf.make_addplot(buy_exit_markers, type='scatter', markersize=80,
|
||||
marker='x', color='darkgreen', label='做多平仓'))
|
||||
if sell_exit_markers.notna().any():
|
||||
add_plots.append(mpf.make_addplot(sell_exit_markers, type='scatter', markersize=80,
|
||||
marker='x', color='darkred', label='做空平仓'))
|
||||
|
||||
# 绘制K线图(更接近交易所风格)
|
||||
market_colors = mpf.make_marketcolors(
|
||||
up='#26a69a', # 常见交易所绿色
|
||||
down='#ef5350', # 常见交易所红色
|
||||
edge='inherit',
|
||||
wick='inherit',
|
||||
volume='inherit'
|
||||
)
|
||||
style = mpf.make_mpf_style(
|
||||
base_mpf_style='binance',
|
||||
marketcolors=market_colors,
|
||||
gridstyle='-',
|
||||
gridcolor='#e6e6e6'
|
||||
)
|
||||
|
||||
fig, axes = mpf.plot(
|
||||
df,
|
||||
type='candle',
|
||||
style=style,
|
||||
title='三分之一回归策略回测',
|
||||
ylabel='价格',
|
||||
addplot=add_plots if add_plots else None,
|
||||
figsize=(16, 9),
|
||||
returnfig=True
|
||||
)
|
||||
|
||||
# 添加图例
|
||||
axes[0].legend(['做多开仓 ▲', '做空开仓 ▼', '做多平仓 ✕', '做空平仓 ✕'], loc='upper left')
|
||||
|
||||
# 标注开仓细节(方向、价格、时间)
|
||||
if trades:
|
||||
ax = axes[0]
|
||||
max_annotate = 60 # 过多会拥挤,可按需调大/调小
|
||||
annotated = 0
|
||||
for i, trade in enumerate(trades):
|
||||
if annotated >= max_annotate:
|
||||
break
|
||||
entry_time = pd.to_datetime(trade['entry_time_ms'], unit='ms')
|
||||
if entry_time not in df.index:
|
||||
continue
|
||||
entry_price = trade['entry']
|
||||
direction = trade['direction']
|
||||
color = 'green' if direction == '做多' else 'red'
|
||||
text = f"{direction} @ {entry_price:.2f}\n{entry_time.strftime('%m-%d %H:%M')}"
|
||||
y_offset = 20 if (i % 2 == 0) else -30
|
||||
ax.annotate(
|
||||
text,
|
||||
xy=(entry_time, entry_price),
|
||||
xytext=(0, y_offset),
|
||||
textcoords='offset points',
|
||||
ha='center',
|
||||
va='bottom' if y_offset > 0 else 'top',
|
||||
fontsize=8,
|
||||
color=color,
|
||||
arrowprops=dict(arrowstyle='->', color=color, lw=0.6, alpha=0.6)
|
||||
)
|
||||
annotated += 1
|
||||
|
||||
if save_path:
|
||||
plt.savefig(save_path, dpi=150, bbox_inches='tight')
|
||||
logger.info(f"图表已保存到: {save_path}")
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
def plot_trades_interactive(all_data: List[Dict], trades: List[Dict], html_path: str = None):
|
||||
"""
|
||||
交互式K线图(TradingView风格:支持缩放、时间区间/价格区间平移缩放)
|
||||
"""
|
||||
if not all_data:
|
||||
logger.warning("没有数据可绘制")
|
||||
return
|
||||
if go is None:
|
||||
logger.warning("未安装 plotly,无法绘制交互式图。请先安装:pip install plotly")
|
||||
return
|
||||
|
||||
df = pd.DataFrame(all_data)
|
||||
df['datetime'] = pd.to_datetime(df['id'], unit='ms')
|
||||
df.sort_values('datetime', inplace=True)
|
||||
|
||||
fig = go.Figure(
|
||||
data=[
|
||||
go.Candlestick(
|
||||
x=df['datetime'],
|
||||
open=df['open'],
|
||||
high=df['high'],
|
||||
low=df['low'],
|
||||
close=df['close'],
|
||||
increasing_line_color='#26a69a',
|
||||
decreasing_line_color='#ef5350',
|
||||
name='K线'
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
# 标注开仓点
|
||||
if trades:
|
||||
entry_x = []
|
||||
entry_y = []
|
||||
entry_text = []
|
||||
entry_color = []
|
||||
for t in trades:
|
||||
entry_x.append(pd.to_datetime(t['entry_time_ms'], unit='ms'))
|
||||
entry_y.append(t['entry'])
|
||||
entry_text.append(f"{t['direction']} @ {t['entry']:.2f}<br>{t['entry_time']}")
|
||||
entry_color.append('#26a69a' if t['direction'] == '做多' else '#ef5350')
|
||||
|
||||
fig.add_trace(
|
||||
go.Scatter(
|
||||
x=entry_x,
|
||||
y=entry_y,
|
||||
mode='markers',
|
||||
marker=dict(size=8, color=entry_color),
|
||||
name='开仓',
|
||||
text=entry_text,
|
||||
hoverinfo='text'
|
||||
)
|
||||
)
|
||||
|
||||
# TradingView风格:深色背景 + 交互缩放
|
||||
fig.update_layout(
|
||||
title='三分之一回归策略回测(交互式)',
|
||||
xaxis=dict(
|
||||
rangeslider=dict(visible=True),
|
||||
type='date',
|
||||
showgrid=False
|
||||
),
|
||||
yaxis=dict(
|
||||
showgrid=False,
|
||||
fixedrange=False
|
||||
),
|
||||
plot_bgcolor='#0b0e11',
|
||||
paper_bgcolor='#0b0e11',
|
||||
font=dict(color='#d1d4dc'),
|
||||
hovermode='x unified',
|
||||
dragmode='zoom'
|
||||
)
|
||||
|
||||
fig.show()
|
||||
if html_path:
|
||||
fig.write_html(html_path)
|
||||
logger.info(f"交互图已保存到: {html_path}")
|
||||
|
||||
|
||||
# ========================= 主程序 =========================
|
||||
if __name__ == '__main__':
|
||||
# ==================== 配置参数 ====================
|
||||
START_DATE = "2025-01-01"
|
||||
END_DATE = "2025-12-31"
|
||||
|
||||
# ==================== 生成日期列表 ====================
|
||||
dates = []
|
||||
if START_DATE and END_DATE:
|
||||
start_dt = datetime.datetime.strptime(START_DATE, '%Y-%m-%d')
|
||||
end_dt = datetime.datetime.strptime(END_DATE, '%Y-%m-%d')
|
||||
|
||||
current_dt = start_dt
|
||||
while current_dt <= end_dt:
|
||||
dates.append(current_dt.strftime('%Y-%m-%d'))
|
||||
current_dt += datetime.timedelta(days=1)
|
||||
|
||||
logger.info(f"查询日期范围:{START_DATE} 到 {END_DATE},共 {len(dates)} 天")
|
||||
|
||||
# ==================== 执行回测 ====================
|
||||
trades, stats, all_data, extra_stats = backtest_one_third_strategy(dates)
|
||||
|
||||
# ==================== 输出结果 ====================
|
||||
logger.info("===== 每笔交易详情 =====")
|
||||
|
||||
contract_size = 10000
|
||||
open_fee_fixed = 5
|
||||
close_fee_rate = 0.0005
|
||||
|
||||
total_points_profit = 0
|
||||
total_money_profit = 0
|
||||
total_fee = 0
|
||||
|
||||
for t in trades:
|
||||
entry = t['entry']
|
||||
exit_p = t['exit']
|
||||
direction = t['direction']
|
||||
|
||||
point_diff = t['diff']
|
||||
money_profit = point_diff / entry * contract_size
|
||||
fee = open_fee_fixed + (contract_size / entry * exit_p * close_fee_rate)
|
||||
net_profit = money_profit - fee
|
||||
|
||||
t.update({
|
||||
'point_diff': point_diff,
|
||||
'raw_profit': money_profit,
|
||||
'fee': fee,
|
||||
'net_profit': net_profit
|
||||
})
|
||||
|
||||
total_points_profit += point_diff
|
||||
total_money_profit += money_profit
|
||||
total_fee += fee
|
||||
|
||||
logger.info(
|
||||
f"{t['entry_time']} {direction} "
|
||||
f"入={entry:.2f} 出={exit_p:.2f} 差价={point_diff:.2f} "
|
||||
f"原始盈利={money_profit:.2f} 手续费={fee:.2f} 净利润={net_profit:.2f}"
|
||||
)
|
||||
|
||||
# ==================== 汇总统计 ====================
|
||||
total_net_profit = total_money_profit - total_fee
|
||||
|
||||
# 计算额外统计
|
||||
win_count = len([t for t in trades if t['diff'] > 0])
|
||||
lose_count = len([t for t in trades if t['diff'] <= 0])
|
||||
total_win_rate = (win_count / len(trades) * 100) if trades else 0
|
||||
|
||||
# 计算平均持仓K线数
|
||||
hold_bars_list = [t.get('hold_bars', 0) for t in trades if 'hold_bars' in t]
|
||||
avg_hold_bars = sum(hold_bars_list) / len(hold_bars_list) if hold_bars_list else 0
|
||||
|
||||
# 计算最大连续亏损
|
||||
max_consecutive_loss = 0
|
||||
current_consecutive_loss = 0
|
||||
for t in trades:
|
||||
if t['diff'] <= 0:
|
||||
current_consecutive_loss += 1
|
||||
max_consecutive_loss = max(max_consecutive_loss, current_consecutive_loss)
|
||||
else:
|
||||
current_consecutive_loss = 0
|
||||
|
||||
# 计算最大回撤
|
||||
cumulative_profit = 0
|
||||
peak = 0
|
||||
max_drawdown = 0
|
||||
for t in trades:
|
||||
cumulative_profit += t.get('net_profit', t['diff'])
|
||||
peak = max(peak, cumulative_profit)
|
||||
drawdown = peak - cumulative_profit
|
||||
max_drawdown = max(max_drawdown, drawdown)
|
||||
|
||||
print(f"\n{'='*60}")
|
||||
print(f"【三分之一回归策略 回测结果】")
|
||||
print(f"{'='*60}")
|
||||
print(f"交易笔数:{len(trades)}")
|
||||
print(f"盈利笔数:{win_count} 亏损笔数:{lose_count}")
|
||||
print(f"总胜率:{total_win_rate:.2f}%")
|
||||
print(f"平均持仓K线数:{avg_hold_bars:.1f}")
|
||||
print(f"最大连续亏损:{max_consecutive_loss} 笔")
|
||||
print(f"{'='*60}")
|
||||
print(f"总点差:{total_points_profit:.2f}")
|
||||
print(f"总原始盈利:{total_money_profit:.2f}")
|
||||
print(f"总手续费:{total_fee:.2f}")
|
||||
print(f"总净利润:{total_net_profit:.2f}")
|
||||
print(f"最大回撤:{max_drawdown:.2f}")
|
||||
print(f"{'='*60}")
|
||||
|
||||
print("\n===== 方向统计 =====")
|
||||
for k, v in stats.items():
|
||||
count = v['count']
|
||||
wins = v['wins']
|
||||
total_p = v['total_profit']
|
||||
win_rate = (wins / count * 100) if count > 0 else 0.0
|
||||
avg_p = (total_p / count) if count > 0 else 0.0
|
||||
print(f"{v['name']}: 次数={count} 胜率={win_rate:.2f}% 总价差={total_p:.2f} 平均价差={avg_p:.2f}")
|
||||
|
||||
print("\n===== 信号统计 =====")
|
||||
print(f"总K线数: {extra_stats['total_bars']}")
|
||||
print(f"无信号K线: {extra_stats['no_signal_bars']} ({extra_stats['no_signal_bars']/extra_stats['total_bars']*100:.1f}%)")
|
||||
print(f"同向信号忽略: {extra_stats['same_dir_ignored']}")
|
||||
|
||||
# ==================== 绘制图表 ====================
|
||||
if trades and all_data:
|
||||
# 如果数据太多,只绘制最近一部分(比如最近500根K线)
|
||||
max_bars = 500
|
||||
if len(all_data) > max_bars:
|
||||
logger.info(f"数据量较大({len(all_data)}条),只绘制最近 {max_bars} 根K线")
|
||||
plot_data = all_data[-max_bars:]
|
||||
# 过滤出在这个时间范围内的交易
|
||||
min_time = datetime.datetime.fromtimestamp(plot_data[0]['id'] / 1000)
|
||||
plot_trades_filtered = [t for t in trades if t['entry_time'] >= min_time]
|
||||
else:
|
||||
plot_data = all_data
|
||||
plot_trades_filtered = trades
|
||||
|
||||
save_path = os.path.join(SCRIPT_DIR, '回测图表.png')
|
||||
plot_trades(plot_data, plot_trades_filtered, save_path=save_path)
|
||||
# 交互式版本(TradingView风格):支持时间区间/价格缩放
|
||||
html_path = os.path.join(SCRIPT_DIR, '回测图表_交互式.html')
|
||||
plot_trades_interactive(plot_data, plot_trades_filtered, html_path=html_path)
|
||||
@@ -1,418 +0,0 @@
|
||||
"""
|
||||
量化交易回测系统 - 30分钟K线策略回测(BitMart数据源)
|
||||
|
||||
========== 策略规则 ==========
|
||||
重要:所有开仓和平仓操作都在下一根K线的开盘价执行
|
||||
|
||||
1. 开仓条件(信号出现时,下一根K线开盘价开仓):
|
||||
- 阳包阴(涨包跌):前一根是跌(阴线),后一根是涨(阳线),且涨的收盘价 > 跌的开盘价
|
||||
-> 下一根K线开盘价开多
|
||||
- 阴包阳(跌包涨):前一根是涨(阳线),后一根是跌(阴线),且跌的收盘价 < 涨的开盘价
|
||||
-> 下一根K线开盘价开空
|
||||
|
||||
2. 平仓条件(所有平仓都在下一根K线开盘价执行):
|
||||
- 持有多单时:遇到两根连续的阴线 -> 下一根K线开盘价平仓
|
||||
- 持有空单时:遇到两根连续的阳线 -> 下一根K线开盘价平仓
|
||||
- 遇到反向信号:下一根K线开盘价平仓并反手开仓
|
||||
|
||||
3. 续持条件:
|
||||
- 遇到同向信号:续持
|
||||
- 未满足平仓条件:续持
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import calendar
|
||||
from dataclasses import dataclass
|
||||
from typing import List, Dict, Optional
|
||||
from loguru import logger
|
||||
from models.bitmart import BitMart30
|
||||
|
||||
|
||||
# ========================= 工具函数 =========================
|
||||
|
||||
def is_bullish(c): # 阳线
|
||||
return float(c['close']) > float(c['open'])
|
||||
|
||||
|
||||
def is_bearish(c): # 阴线
|
||||
return float(c['close']) < float(c['open'])
|
||||
|
||||
|
||||
def check_signal(prev, curr):
|
||||
"""
|
||||
包住形态信号判定(优化版):
|
||||
只看两种信号,严格按照收盘价与开盘价的比较:
|
||||
|
||||
1. 跌包涨(前涨后跌)-> 做空:
|
||||
- 前一根是涨(阳线:close > open)
|
||||
- 后一根是跌(阴线:close < open)
|
||||
- 且:跌的收盘价 < 涨的开盘价(curr['close'] < prev['open'])
|
||||
|
||||
2. 涨包跌(前跌后涨)-> 做多:
|
||||
- 前一根是跌(阴线:close < open)
|
||||
- 后一根是涨(阳线:close > open)
|
||||
- 且:涨的收盘价 > 跌的开盘价(curr['close'] > prev['open'])
|
||||
"""
|
||||
p_open = float(prev['open'])
|
||||
c_close = float(curr['close'])
|
||||
|
||||
# 跌包涨(前涨后跌) -> 做空:跌的收盘价 < 涨的开盘价
|
||||
if is_bullish(prev) and is_bearish(curr) and c_close < p_open:
|
||||
return "short", "bull_bear_engulf"
|
||||
|
||||
# 涨包跌(前跌后涨) -> 做多:涨的收盘价 > 跌的开盘价
|
||||
if is_bearish(prev) and is_bullish(curr) and c_close > p_open:
|
||||
return "long", "bear_bull_engulf"
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
def get_data_by_date(model, date_str: str):
|
||||
"""
|
||||
按天获取指定表的数据(30分钟K线)
|
||||
数据格式:时间戳(毫秒级) 开盘价 最高价 最低价 收盘价
|
||||
例如:1767461400000 3106.68 3109.1 3106.22 3107.22
|
||||
|
||||
注意:返回的数据已按时间戳(id)升序排序
|
||||
"""
|
||||
try:
|
||||
target_date = datetime.datetime.strptime(date_str, '%Y-%m-%d')
|
||||
except ValueError:
|
||||
logger.error("日期格式不正确,请使用 YYYY-MM-DD 格式。")
|
||||
return []
|
||||
|
||||
# 将日期转换为毫秒级时间戳进行查询
|
||||
start_ts = int(target_date.timestamp() * 1000)
|
||||
end_ts = int((target_date + datetime.timedelta(days=1)).timestamp() * 1000) - 1
|
||||
|
||||
# 查询时按时间戳升序排序
|
||||
query = model.select().where(model.id.between(start_ts, end_ts)).order_by(model.id.asc())
|
||||
data = [{'id': i.id, 'open': i.open, 'high': i.high, 'low': i.low, 'close': i.close} for i in query]
|
||||
|
||||
# 确保数据已排序
|
||||
if data:
|
||||
data.sort(key=lambda x: x['id'])
|
||||
|
||||
return data
|
||||
|
||||
|
||||
# ========================= 回测逻辑 =========================
|
||||
|
||||
def backtest_15m_trend_optimized(dates: List[str]):
|
||||
all_data: List[Dict] = []
|
||||
total_queried = 0
|
||||
for d in dates:
|
||||
day_data = get_data_by_date(BitMart30, d)
|
||||
all_data.extend(day_data)
|
||||
if day_data:
|
||||
total_queried += len(day_data)
|
||||
|
||||
logger.info(f"总共查询了 {len(dates)} 天,获取到 {total_queried} 条K线数据")
|
||||
|
||||
if not all_data:
|
||||
logger.warning("未获取到任何数据,请检查:")
|
||||
logger.warning("1. 数据库连接是否正常")
|
||||
logger.warning("2. 查询的日期范围是否在数据范围内")
|
||||
logger.warning("3. 时间戳格式是否正确(毫秒级)")
|
||||
return [], {
|
||||
'bear_bull_engulf': {'count': 0, 'wins': 0, 'total_profit': 0.0, 'name': '涨包跌'},
|
||||
'bull_bear_engulf': {'count': 0, 'wins': 0, 'total_profit': 0.0, 'name': '跌包涨'},
|
||||
}
|
||||
|
||||
# 重要:合并所有数据后,必须先按时间戳(id)排序
|
||||
all_data.sort(key=lambda x: x['id'])
|
||||
|
||||
# 验证排序结果
|
||||
if len(all_data) > 1:
|
||||
first_ts = all_data[0]['id']
|
||||
last_ts = all_data[-1]['id']
|
||||
first_time = datetime.datetime.fromtimestamp(first_ts / 1000)
|
||||
last_time = datetime.datetime.fromtimestamp(last_ts / 1000)
|
||||
logger.info(f"数据已按时间排序:{first_time.strftime('%Y-%m-%d %H:%M:%S')} 到 {last_time.strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
|
||||
stats = {
|
||||
'bear_bull_engulf': {'count': 0, 'wins': 0, 'total_profit': 0.0, 'name': '涨包跌'},
|
||||
'bull_bear_engulf': {'count': 0, 'wins': 0, 'total_profit': 0.0, 'name': '跌包涨'},
|
||||
}
|
||||
|
||||
trades: List[Dict] = []
|
||||
current_position: Optional[Dict] = None # 开仓信息
|
||||
consecutive_opposite_count = 0 # 连续反色K线计数
|
||||
idx = 1
|
||||
|
||||
while idx < len(all_data) - 1:
|
||||
prev, curr, next_bar = all_data[idx - 1], all_data[idx], all_data[idx + 1]
|
||||
direction, signal_key = check_signal(prev, curr)
|
||||
|
||||
# 空仓 -> 碰到信号则开仓(下一根K线开盘价)
|
||||
if current_position is None:
|
||||
if direction:
|
||||
entry_price = float(next_bar['open'])
|
||||
current_position = {
|
||||
'direction': direction,
|
||||
'signal': stats[signal_key]['name'],
|
||||
'signal_key': signal_key,
|
||||
'entry_price': entry_price,
|
||||
'entry_time': next_bar['id']
|
||||
}
|
||||
consecutive_opposite_count = 0 # 重置连续反色计数
|
||||
stats[signal_key]['count'] += 1
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
# 有仓位状态:检查平仓条件
|
||||
pos_dir = current_position['direction']
|
||||
pos_sig_key = current_position['signal_key']
|
||||
|
||||
# 1. 反向信号 -> 下一根K线开盘价平仓并反手开仓
|
||||
if direction and direction != pos_dir:
|
||||
exit_price = float(next_bar['open'])
|
||||
diff = (exit_price - current_position['entry_price']) if pos_dir == 'long' else (
|
||||
current_position['entry_price'] - exit_price)
|
||||
trades.append({
|
||||
'entry_time': datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
'exit_time': datetime.datetime.fromtimestamp(next_bar['id'] / 1000),
|
||||
'signal': current_position['signal'],
|
||||
'direction': '做多' if pos_dir == 'long' else '做空',
|
||||
'entry': current_position['entry_price'],
|
||||
'exit': exit_price,
|
||||
'diff': diff
|
||||
})
|
||||
stats[pos_sig_key]['total_profit'] += diff
|
||||
if diff > 0: stats[pos_sig_key]['wins'] += 1
|
||||
|
||||
# 反手开仓
|
||||
current_position = {
|
||||
'direction': direction,
|
||||
'signal': stats[signal_key]['name'],
|
||||
'signal_key': signal_key,
|
||||
'entry_price': exit_price,
|
||||
'entry_time': next_bar['id']
|
||||
}
|
||||
consecutive_opposite_count = 0 # 重置连续反色计数
|
||||
stats[signal_key]['count'] += 1
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
# 2. 检查连续反色K线平仓条件(下一根K线开盘价平仓)
|
||||
# 持有多单:检查是否连续两根阴线
|
||||
if pos_dir == 'long' and is_bearish(curr):
|
||||
consecutive_opposite_count += 1
|
||||
# 如果已经连续两根阴线,下一根K线开盘价平仓
|
||||
if consecutive_opposite_count >= 2:
|
||||
exit_price = float(next_bar['open'])
|
||||
diff = exit_price - current_position['entry_price']
|
||||
trades.append({
|
||||
'entry_time': datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
'exit_time': datetime.datetime.fromtimestamp(next_bar['id'] / 1000),
|
||||
'signal': current_position['signal'],
|
||||
'direction': '做多',
|
||||
'entry': current_position['entry_price'],
|
||||
'exit': exit_price,
|
||||
'diff': diff
|
||||
})
|
||||
stats[pos_sig_key]['total_profit'] += diff
|
||||
if diff > 0: stats[pos_sig_key]['wins'] += 1
|
||||
current_position = None
|
||||
consecutive_opposite_count = 0
|
||||
idx += 1
|
||||
continue
|
||||
else:
|
||||
# 只有一根阴线,续持
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
# 持有空单:检查是否连续两根阳线
|
||||
elif pos_dir == 'short' and is_bullish(curr):
|
||||
consecutive_opposite_count += 1
|
||||
# 如果已经连续两根阳线,下一根K线开盘价平仓
|
||||
if consecutive_opposite_count >= 2:
|
||||
exit_price = float(next_bar['open'])
|
||||
diff = current_position['entry_price'] - exit_price
|
||||
trades.append({
|
||||
'entry_time': datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
'exit_time': datetime.datetime.fromtimestamp(next_bar['id'] / 1000),
|
||||
'signal': current_position['signal'],
|
||||
'direction': '做空',
|
||||
'entry': current_position['entry_price'],
|
||||
'exit': exit_price,
|
||||
'diff': diff
|
||||
})
|
||||
stats[pos_sig_key]['total_profit'] += diff
|
||||
if diff > 0: stats[pos_sig_key]['wins'] += 1
|
||||
current_position = None
|
||||
consecutive_opposite_count = 0
|
||||
idx += 1
|
||||
continue
|
||||
else:
|
||||
# 只有一根阳线,续持
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
# 3. 同向K线或同向信号 -> 续持,重置连续反色计数
|
||||
if (pos_dir == 'long' and is_bullish(curr)) or (pos_dir == 'short' and is_bearish(curr)):
|
||||
consecutive_opposite_count = 0 # 重置连续反色计数
|
||||
|
||||
# 同向信号 -> 续持
|
||||
if direction and direction == pos_dir:
|
||||
consecutive_opposite_count = 0 # 重置连续反色计数
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
idx += 1
|
||||
|
||||
# 尾仓:最后一根收盘价平仓
|
||||
if current_position:
|
||||
last = all_data[-1]
|
||||
exit_price = float(last['close'])
|
||||
pos_dir = current_position['direction']
|
||||
diff = (exit_price - current_position['entry_price']) if pos_dir == 'long' else (
|
||||
current_position['entry_price'] - exit_price)
|
||||
trades.append({
|
||||
'entry_time': datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
'exit_time': datetime.datetime.fromtimestamp(last['id'] / 1000),
|
||||
'signal': current_position['signal'],
|
||||
'direction': '做多' if pos_dir == 'long' else '做空',
|
||||
'entry': current_position['entry_price'],
|
||||
'exit': exit_price,
|
||||
'diff': diff
|
||||
})
|
||||
stats[current_position['signal_key']]['total_profit'] += diff
|
||||
if diff > 0: stats[current_position['signal_key']]['wins'] += 1
|
||||
|
||||
return trades, stats
|
||||
|
||||
|
||||
# ========================= 运行示例(优化版盈利计算) =========================
|
||||
if __name__ == '__main__':
|
||||
# ==================== 配置参数:指定查询时间范围 ====================
|
||||
# 方式1:指定开始和结束日期(推荐)
|
||||
START_DATE = "2025-01-01" # 开始日期,格式:YYYY-MM-DD
|
||||
END_DATE = "2025-12-31" # 结束日期,格式:YYYY-MM-DD
|
||||
|
||||
# 方式2:如果上面两个为空,则使用年份和月份范围
|
||||
START_YEAR = None # 开始年份,例如:2025
|
||||
START_MONTH = None # 开始月份,例如:1
|
||||
END_YEAR = None # 结束年份,例如:2025
|
||||
END_MONTH = None # 结束月份,例如:12
|
||||
|
||||
# ==================== 生成查询日期列表 ====================
|
||||
dates = []
|
||||
|
||||
# 优先使用指定的日期范围
|
||||
if START_DATE and END_DATE:
|
||||
try:
|
||||
start_dt = datetime.datetime.strptime(START_DATE, '%Y-%m-%d')
|
||||
end_dt = datetime.datetime.strptime(END_DATE, '%Y-%m-%d')
|
||||
|
||||
if start_dt > end_dt:
|
||||
logger.error(f"开始日期 {START_DATE} 不能晚于结束日期 {END_DATE}")
|
||||
exit(1)
|
||||
|
||||
current_dt = start_dt
|
||||
while current_dt <= end_dt:
|
||||
dates.append(current_dt.strftime('%Y-%m-%d'))
|
||||
current_dt += datetime.timedelta(days=1)
|
||||
|
||||
logger.info(f"使用指定日期范围:{START_DATE} 到 {END_DATE},共 {len(dates)} 天")
|
||||
except ValueError as e:
|
||||
logger.error(f"日期格式错误:{e},请使用 YYYY-MM-DD 格式")
|
||||
exit(1)
|
||||
|
||||
# 如果未指定日期范围,使用年份和月份范围
|
||||
elif START_YEAR and END_YEAR:
|
||||
start_m = START_MONTH if START_MONTH else 1
|
||||
end_m = END_MONTH if END_MONTH else 12
|
||||
|
||||
for year in range(START_YEAR, END_YEAR + 1):
|
||||
month_start = start_m if year == START_YEAR else 1
|
||||
month_end = end_m if year == END_YEAR else 12
|
||||
|
||||
for month in range(month_start, month_end + 1):
|
||||
days_in_month = calendar.monthrange(year, month)[1]
|
||||
for day in range(1, days_in_month + 1):
|
||||
dates.append(f"{year}-{month:02d}-{day:02d}")
|
||||
|
||||
logger.info(f"使用年份月份范围:{START_YEAR}年{start_m}月 到 {END_YEAR}年{end_m}月,共 {len(dates)} 天")
|
||||
|
||||
# 如果都没有指定,使用默认范围
|
||||
else:
|
||||
logger.warning("未指定日期范围,使用默认:2025年1-12月")
|
||||
for month in range(1, 13):
|
||||
days_in_month = calendar.monthrange(2025, month)[1]
|
||||
for day in range(1, days_in_month + 1):
|
||||
dates.append(f"2025-{month:02d}-{day:02d}")
|
||||
|
||||
if dates:
|
||||
logger.info(f"准备查询 {len(dates)} 天的数据,日期范围:{dates[0]} 到 {dates[-1]}")
|
||||
else:
|
||||
logger.error("未生成任何查询日期,请检查配置参数")
|
||||
exit(1)
|
||||
|
||||
trades, stats = backtest_15m_trend_optimized(dates)
|
||||
|
||||
logger.info("===== 每笔交易详情 =====")
|
||||
|
||||
# === 参数设定 ===
|
||||
contract_size = 10000 # 合约规模(1手对应多少基础货币)
|
||||
open_fee_fixed = 5 # 固定开仓手续费
|
||||
close_fee_rate = 0.0005 # 按成交额比例的平仓手续费率
|
||||
|
||||
total_points_profit = 0 # 累计点差
|
||||
total_money_profit = 0 # 累计金额盈利
|
||||
total_fee = 0 # 累计手续费
|
||||
|
||||
for t in trades:
|
||||
entry = t['entry']
|
||||
exit = t['exit']
|
||||
direction = t['direction']
|
||||
|
||||
# === 1️⃣ 原始价差(点差) ===
|
||||
point_diff = (exit - entry) if direction == '做多' else (entry - exit)
|
||||
|
||||
# === 2️⃣ 金额盈利(考虑合约规模) ===
|
||||
money_profit = point_diff / entry * contract_size # 利润以基础货币计(例如USD)
|
||||
|
||||
# === 3️⃣ 手续费计算 ===
|
||||
# 开仓 + 平仓手续费(按比例计算 + 固定)
|
||||
fee = open_fee_fixed + (contract_size / entry * exit * close_fee_rate)
|
||||
|
||||
# === 4️⃣ 净利润 ===
|
||||
net_profit = money_profit - fee
|
||||
|
||||
# 保存计算结果
|
||||
t.update({
|
||||
'point_diff': point_diff,
|
||||
'raw_profit': money_profit,
|
||||
'fee': fee,
|
||||
'net_profit': net_profit
|
||||
})
|
||||
|
||||
total_points_profit += point_diff
|
||||
total_money_profit += money_profit
|
||||
total_fee += fee
|
||||
|
||||
# if net_profit < -400:
|
||||
logger.info(
|
||||
f"{t['entry_time']} {direction}({t['signal']}) "
|
||||
f"入={entry:.2f} 出={exit:.2f} 差价={point_diff:.2f} "
|
||||
f"原始盈利={money_profit:.2f} 手续费={fee:.2f} 净利润={net_profit:.2f} {t['exit_time']}"
|
||||
)
|
||||
|
||||
# === 汇总统计 ===
|
||||
total_net_profit = total_money_profit - total_fee
|
||||
print(f"\n【BitMart 回测结果】")
|
||||
print(f"一共交易笔数:{len(trades)}")
|
||||
print(f"总点差:{total_points_profit:.2f}")
|
||||
print(f"总原始盈利(未扣费):{total_money_profit:.2f}")
|
||||
print(f"总手续费:{total_fee:.2f}")
|
||||
print(f"总净利润:{total_net_profit:.2f}\n")
|
||||
|
||||
print(total_money_profit - total_fee * 0.1)
|
||||
|
||||
print("===== 信号统计 =====")
|
||||
for k, v in stats.items():
|
||||
name, count, wins, total_p = v['name'], v['count'], v['wins'], v['total_profit']
|
||||
win_rate = (wins / count * 100) if count > 0 else 0.0
|
||||
avg_p = (total_p / count) if count > 0 else 0.0
|
||||
print(f"{name}: 次数={count} 胜率={win_rate:.2f}% 总价差={total_p:.2f} 平均价差={avg_p:.2f}")
|
||||
@@ -1,7 +1,8 @@
|
||||
"""
|
||||
BitMart 多周期K线数据抓取脚本
|
||||
支持同时获取 1分钟、3分钟、5分钟、15分钟、30分钟、1小时 K线数据
|
||||
自动创建对应的数据库表
|
||||
支持秒级价格数据(通过成交记录API)
|
||||
支持断点续传,从数据库最新/最早记录继续抓取
|
||||
"""
|
||||
|
||||
import time
|
||||
@@ -26,6 +27,34 @@ KLINE_CONFIGS = {
|
||||
}
|
||||
|
||||
|
||||
class BitMartETHTrades(Model):
|
||||
"""成交记录模型(秒级/毫秒级原始数据)"""
|
||||
id = BigIntegerField(primary_key=True) # 成交ID
|
||||
timestamp = BigIntegerField(index=True) # 成交时间戳(毫秒)
|
||||
price = FloatField() # 成交价格
|
||||
volume = FloatField() # 成交量
|
||||
side = IntegerField() # 方向: 1=买, -1=卖
|
||||
|
||||
class Meta:
|
||||
database = db
|
||||
table_name = 'bitmart_eth_trades'
|
||||
|
||||
|
||||
class BitMartETHSecond(Model):
|
||||
"""秒级K线模型(由成交记录聚合而来)"""
|
||||
id = BigIntegerField(primary_key=True) # 时间戳(毫秒,取整到秒)
|
||||
open = FloatField(null=True)
|
||||
high = FloatField(null=True)
|
||||
low = FloatField(null=True)
|
||||
close = FloatField(null=True)
|
||||
volume = FloatField(null=True)
|
||||
trade_count = IntegerField(null=True) # 该秒内成交笔数
|
||||
|
||||
class Meta:
|
||||
database = db
|
||||
table_name = 'bitmart_eth_1s'
|
||||
|
||||
|
||||
def create_kline_model(step: int):
|
||||
"""
|
||||
动态创建K线数据模型
|
||||
@@ -85,67 +114,100 @@ class BitMartMultiKlineCollector:
|
||||
# 创建表(如果不存在)
|
||||
db.create_tables([model], safe=True)
|
||||
logger.info(f"初始化表: {model._meta.table_name}")
|
||||
|
||||
# 创建成交记录表和秒级K线表
|
||||
db.create_tables([BitMartETHTrades, BitMartETHSecond], safe=True)
|
||||
logger.info(f"初始化表: bitmart_eth_trades (成交记录)")
|
||||
logger.info(f"初始化表: bitmart_eth_1s (秒级K线)")
|
||||
|
||||
def get_klines(self, step: int, start_time: int, end_time: int):
|
||||
def get_db_time_range(self, step: int):
|
||||
"""
|
||||
获取K线数据
|
||||
获取数据库中已有数据的时间范围
|
||||
:param step: K线周期
|
||||
:return: (earliest_ts, latest_ts) 毫秒时间戳,无数据返回 (None, None)
|
||||
"""
|
||||
model = self.models.get(step)
|
||||
if not model:
|
||||
return None, None
|
||||
|
||||
try:
|
||||
# 获取最早记录
|
||||
earliest = model.select(fn.MIN(model.id)).scalar()
|
||||
# 获取最新记录
|
||||
latest = model.select(fn.MAX(model.id)).scalar()
|
||||
return earliest, latest
|
||||
except Exception as e:
|
||||
logger.error(f"查询数据库时间范围异常: {e}")
|
||||
return None, None
|
||||
|
||||
def get_klines(self, step: int, start_time: int, end_time: int, max_retries: int = 3):
|
||||
"""
|
||||
获取K线数据(带重试)
|
||||
:param step: K线周期(分钟)
|
||||
:param start_time: 开始时间戳(秒级)
|
||||
:param end_time: 结束时间戳(秒级)
|
||||
:param max_retries: 最大重试次数
|
||||
:return: K线数据列表
|
||||
"""
|
||||
try:
|
||||
# 确保是整数
|
||||
start_time = int(start_time)
|
||||
end_time = int(end_time)
|
||||
|
||||
logger.debug(f"API请求: step={step}, start={start_time}, end={end_time}")
|
||||
|
||||
response = self.contractAPI.get_kline(
|
||||
contract_symbol=self.contract_symbol,
|
||||
step=step,
|
||||
start_time=start_time,
|
||||
end_time=end_time
|
||||
)[0]
|
||||
|
||||
if response['code'] != 1000:
|
||||
logger.error(f"获取 {step}分钟 K线失败: {response}")
|
||||
for attempt in range(max_retries):
|
||||
try:
|
||||
start_time = int(start_time)
|
||||
end_time = int(end_time)
|
||||
|
||||
response = self.contractAPI.get_kline(
|
||||
contract_symbol=self.contract_symbol,
|
||||
step=step,
|
||||
start_time=start_time,
|
||||
end_time=end_time
|
||||
)[0]
|
||||
|
||||
if response['code'] != 1000:
|
||||
logger.warning(f"API返回错误 (尝试 {attempt+1}/{max_retries}): {response}")
|
||||
if attempt < max_retries - 1:
|
||||
time.sleep(1)
|
||||
continue
|
||||
return []
|
||||
|
||||
klines = response.get('data', [])
|
||||
formatted = []
|
||||
for k in klines:
|
||||
timestamp_ms = int(k["timestamp"]) * 1000
|
||||
formatted.append({
|
||||
'id': timestamp_ms,
|
||||
'open': float(k["open_price"]),
|
||||
'high': float(k["high_price"]),
|
||||
'low': float(k["low_price"]),
|
||||
'close': float(k["close_price"])
|
||||
})
|
||||
|
||||
formatted.sort(key=lambda x: x['id'])
|
||||
return formatted
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"获取K线异常 (尝试 {attempt+1}/{max_retries}): {e}")
|
||||
if attempt < max_retries - 1:
|
||||
time.sleep(2)
|
||||
continue
|
||||
return []
|
||||
|
||||
klines = response.get('data', [])
|
||||
formatted = []
|
||||
for k in klines:
|
||||
timestamp_ms = int(k["timestamp"]) * 1000
|
||||
formatted.append({
|
||||
'id': timestamp_ms,
|
||||
'open': float(k["open_price"]),
|
||||
'high': float(k["high_price"]),
|
||||
'low': float(k["low_price"]),
|
||||
'close': float(k["close_price"])
|
||||
})
|
||||
|
||||
formatted.sort(key=lambda x: x['id'])
|
||||
return formatted
|
||||
except Exception as e:
|
||||
logger.error(f"获取 {step}分钟 K线异常: {e}")
|
||||
return []
|
||||
|
||||
return []
|
||||
|
||||
def save_klines(self, step: int, klines: list):
|
||||
"""
|
||||
保存K线数据到数据库
|
||||
:param step: K线周期
|
||||
:param klines: K线数据列表
|
||||
:return: 保存的数量
|
||||
:return: 新保存的数量
|
||||
"""
|
||||
model = self.models.get(step)
|
||||
if not model:
|
||||
logger.error(f"未找到 {step}分钟 的数据模型")
|
||||
return 0
|
||||
|
||||
saved_count = 0
|
||||
new_count = 0
|
||||
for kline in klines:
|
||||
try:
|
||||
model.get_or_create(
|
||||
_, created = model.get_or_create(
|
||||
id=kline['id'],
|
||||
defaults={
|
||||
'open': kline['open'],
|
||||
@@ -154,87 +216,164 @@ class BitMartMultiKlineCollector:
|
||||
'close': kline['close'],
|
||||
}
|
||||
)
|
||||
saved_count += 1
|
||||
if created:
|
||||
new_count += 1
|
||||
except Exception as e:
|
||||
logger.error(f"保存 {step}分钟 K线数据失败 {kline['id']}: {e}")
|
||||
logger.error(f"保存K线数据失败 {kline['id']}: {e}")
|
||||
|
||||
return saved_count
|
||||
return new_count
|
||||
|
||||
def collect_single_period(self, step: int, start_date: str = None, days: int = None):
|
||||
def get_batch_seconds(self, step: int):
|
||||
"""根据周期获取合适的批次大小"""
|
||||
if step == 1:
|
||||
return 3600 * 4 # 1分钟: 每次4小时
|
||||
elif step == 3:
|
||||
return 3600 * 8 # 3分钟: 每次8小时
|
||||
elif step == 5:
|
||||
return 3600 * 12 # 5分钟: 每次12小时
|
||||
elif step == 15:
|
||||
return 3600 * 24 # 15分钟: 每次1天
|
||||
elif step == 30:
|
||||
return 3600 * 48 # 30分钟: 每次2天
|
||||
else:
|
||||
return 3600 * 72 # 1小时: 每次3天
|
||||
|
||||
def collect_period_range(self, step: int, target_start: int, target_end: int):
|
||||
"""
|
||||
抓取单个周期的历史数据(从当前时间向前抓取,直到遇到API限制)
|
||||
抓取指定时间范围的K线数据(支持断点续传)
|
||||
:param step: K线周期(分钟)
|
||||
:param start_date: 起始日期 'YYYY-MM-DD'(目标,可能无法达到)
|
||||
:param days: 抓取天数(目标,可能无法达到)
|
||||
:param target_start: 目标开始时间戳(秒)
|
||||
:param target_end: 目标结束时间戳(秒)
|
||||
:return: 保存的总数量
|
||||
"""
|
||||
suffix = KLINE_CONFIGS.get(step, f'{step}m')
|
||||
now = int(time.time())
|
||||
batch_seconds = self.get_batch_seconds(step)
|
||||
|
||||
if start_date:
|
||||
start_dt = datetime.datetime.strptime(start_date, '%Y-%m-%d')
|
||||
target_start_time = int(start_dt.timestamp())
|
||||
logger.info(f"开始抓取 {suffix} K线数据: 目标从 {start_date} 开始(从现在向前抓取)")
|
||||
elif days:
|
||||
target_start_time = now - 3600 * 24 * days
|
||||
logger.info(f"开始抓取 {suffix} K线数据: 目标最近 {days} 天")
|
||||
else:
|
||||
target_start_time = now - 3600 * 24 * 30
|
||||
logger.info(f"开始抓取 {suffix} K线数据: 目标最近 30 天")
|
||||
# 获取数据库已有数据范围
|
||||
db_earliest, db_latest = self.get_db_time_range(step)
|
||||
|
||||
# 根据周期调整批次大小
|
||||
if step <= 5:
|
||||
batch_seconds = 3600 * 6 # 小周期每次6小时
|
||||
elif step <= 30:
|
||||
batch_seconds = 3600 * 24 # 中周期每次1天
|
||||
if db_earliest and db_latest:
|
||||
db_earliest_sec = db_earliest // 1000
|
||||
db_latest_sec = db_latest // 1000
|
||||
logger.info(f"[{suffix}] 数据库已有数据: "
|
||||
f"{time.strftime('%Y-%m-%d %H:%M', time.localtime(db_earliest_sec))} ~ "
|
||||
f"{time.strftime('%Y-%m-%d %H:%M', time.localtime(db_latest_sec))}")
|
||||
else:
|
||||
batch_seconds = 3600 * 24 * 3 # 大周期每次3天
|
||||
db_earliest_sec = None
|
||||
db_latest_sec = None
|
||||
logger.info(f"[{suffix}] 数据库暂无数据")
|
||||
|
||||
total_saved = 0
|
||||
fail_count = 0
|
||||
max_fail = 3
|
||||
|
||||
# 从当前时间向前抓取
|
||||
current_end = now
|
||||
while current_end > target_start_time:
|
||||
current_start = current_end - batch_seconds
|
||||
|
||||
# 打印时间范围
|
||||
start_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(current_start))
|
||||
end_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(current_end))
|
||||
logger.info(f"[{suffix}] 抓取: {start_str} -> {end_str}")
|
||||
|
||||
klines = self.get_klines(step, current_start, current_end)
|
||||
if klines:
|
||||
saved = self.save_klines(step, klines)
|
||||
total_saved += saved
|
||||
logger.info(f"[{suffix}] 保存 {saved} 条,累计 {total_saved} 条")
|
||||
fail_count = 0
|
||||
else:
|
||||
fail_count += 1
|
||||
logger.warning(f"[{suffix}] 未获取到数据 (连续失败 {fail_count} 次)")
|
||||
if fail_count >= max_fail:
|
||||
earliest = time.strftime('%Y-%m-%d', time.localtime(current_end))
|
||||
logger.warning(f"[{suffix}] 达到API历史数据限制,最早可用数据约 {earliest}")
|
||||
break
|
||||
|
||||
current_end = current_start
|
||||
time.sleep(0.3) # API请求间隔
|
||||
# === 第一阶段:向前抓取历史数据(从数据库最早记录向前,直到 target_start)===
|
||||
if db_earliest_sec:
|
||||
backward_end = db_earliest_sec
|
||||
else:
|
||||
backward_end = target_end
|
||||
|
||||
if backward_end > target_start:
|
||||
logger.info(f"[{suffix}] === 开始向前抓取历史数据 ===")
|
||||
total_backward = backward_end - target_start
|
||||
|
||||
current_end = backward_end
|
||||
fail_count = 0
|
||||
max_fail = 5
|
||||
|
||||
while current_end > target_start and fail_count < max_fail:
|
||||
current_start = max(current_end - batch_seconds, target_start)
|
||||
|
||||
# 计算进度
|
||||
progress = (backward_end - current_end) / total_backward * 100 if total_backward > 0 else 0
|
||||
start_str = time.strftime('%Y-%m-%d %H:%M', time.localtime(current_start))
|
||||
end_str = time.strftime('%Y-%m-%d %H:%M', time.localtime(current_end))
|
||||
|
||||
klines = self.get_klines(step, current_start, current_end)
|
||||
if klines:
|
||||
saved = self.save_klines(step, klines)
|
||||
total_saved += saved
|
||||
logger.info(f"[{suffix}] ← 历史 {start_str} ~ {end_str} | "
|
||||
f"获取 {len(klines)} 条, 新增 {saved} 条 | 进度 {progress:.1f}%")
|
||||
fail_count = 0
|
||||
else:
|
||||
fail_count += 1
|
||||
logger.warning(f"[{suffix}] ← 历史 {start_str} 无数据 (连续失败 {fail_count}/{max_fail})")
|
||||
if fail_count >= max_fail:
|
||||
earliest_date = time.strftime('%Y-%m-%d', time.localtime(current_end))
|
||||
logger.warning(f"[{suffix}] 已达到API历史数据限制,最早可获取: {earliest_date}")
|
||||
break
|
||||
|
||||
current_end = current_start
|
||||
time.sleep(0.3)
|
||||
|
||||
# === 第二阶段:向后抓取最新数据(从数据库最新记录向后,直到 target_end)===
|
||||
if db_latest_sec:
|
||||
forward_start = db_latest_sec
|
||||
else:
|
||||
# 如果没有数据,从第一阶段结束的地方开始
|
||||
forward_start = target_start
|
||||
|
||||
if forward_start < target_end:
|
||||
logger.info(f"[{suffix}] === 开始向后抓取最新数据 ===")
|
||||
total_forward = target_end - forward_start
|
||||
|
||||
current_start = forward_start
|
||||
fail_count = 0
|
||||
max_fail = 3
|
||||
|
||||
while current_start < target_end and fail_count < max_fail:
|
||||
current_end = min(current_start + batch_seconds, target_end)
|
||||
|
||||
# 计算进度
|
||||
progress = (current_start - forward_start) / total_forward * 100 if total_forward > 0 else 0
|
||||
start_str = time.strftime('%Y-%m-%d %H:%M', time.localtime(current_start))
|
||||
end_str = time.strftime('%Y-%m-%d %H:%M', time.localtime(current_end))
|
||||
|
||||
klines = self.get_klines(step, current_start, current_end)
|
||||
if klines:
|
||||
saved = self.save_klines(step, klines)
|
||||
total_saved += saved
|
||||
logger.info(f"[{suffix}] → 最新 {start_str} ~ {end_str} | "
|
||||
f"获取 {len(klines)} 条, 新增 {saved} 条 | 进度 {progress:.1f}%")
|
||||
fail_count = 0
|
||||
else:
|
||||
fail_count += 1
|
||||
logger.warning(f"[{suffix}] → 最新 {start_str} 无数据 (失败 {fail_count}/{max_fail})")
|
||||
|
||||
current_start = current_end
|
||||
time.sleep(0.3)
|
||||
|
||||
# 统计最终数据范围
|
||||
final_earliest, final_latest = self.get_db_time_range(step)
|
||||
if final_earliest and final_latest:
|
||||
logger.success(f"[{suffix}] 抓取完成!本次新增 {total_saved} 条 | 数据范围: "
|
||||
f"{time.strftime('%Y-%m-%d', time.localtime(final_earliest//1000))} ~ "
|
||||
f"{time.strftime('%Y-%m-%d', time.localtime(final_latest//1000))}")
|
||||
else:
|
||||
logger.success(f"[{suffix}] 抓取完成!本次新增 {total_saved} 条")
|
||||
|
||||
logger.success(f"[{suffix}] 抓取完成,共保存 {total_saved} 条数据")
|
||||
return total_saved
|
||||
|
||||
def collect_all_periods(self, start_date: str = None, days: int = None,
|
||||
periods: list = None):
|
||||
def collect_from_date(self, start_date: str, periods: list = None):
|
||||
"""
|
||||
抓取所有周期的历史数据
|
||||
从指定日期抓取到当前时间
|
||||
:param start_date: 起始日期 'YYYY-MM-DD'
|
||||
:param days: 抓取天数
|
||||
:param periods: 要抓取的周期列表,如 [1, 5, 15],默认全部
|
||||
"""
|
||||
if periods is None:
|
||||
periods = list(KLINE_CONFIGS.keys())
|
||||
|
||||
logger.info(f"开始抓取多周期K线数据,周期: {[KLINE_CONFIGS[p] for p in periods]}")
|
||||
# 计算时间范围
|
||||
start_dt = datetime.datetime.strptime(start_date, '%Y-%m-%d')
|
||||
target_start = int(start_dt.timestamp())
|
||||
target_end = int(time.time())
|
||||
|
||||
start_str = start_dt.strftime('%Y-%m-%d')
|
||||
end_str = datetime.datetime.now().strftime('%Y-%m-%d %H:%M')
|
||||
|
||||
logger.info(f"{'='*60}")
|
||||
logger.info(f"目标时间范围: {start_str} ~ {end_str}")
|
||||
logger.info(f"抓取周期: {[KLINE_CONFIGS[p] for p in periods]}")
|
||||
logger.info(f"{'='*60}")
|
||||
|
||||
results = {}
|
||||
for step in periods:
|
||||
@@ -242,23 +381,347 @@ class BitMartMultiKlineCollector:
|
||||
logger.warning(f"不支持的周期: {step}分钟,跳过")
|
||||
continue
|
||||
|
||||
logger.info(f"\n{'='*50}")
|
||||
logger.info(f"\n{'='*60}")
|
||||
logger.info(f"开始抓取 {KLINE_CONFIGS[step]} K线")
|
||||
logger.info(f"{'='*50}")
|
||||
logger.info(f"{'='*60}")
|
||||
|
||||
saved = self.collect_single_period(step, start_date, days)
|
||||
saved = self.collect_period_range(step, target_start, target_end)
|
||||
results[KLINE_CONFIGS[step]] = saved
|
||||
|
||||
time.sleep(1) # 不同周期之间间隔
|
||||
|
||||
logger.info(f"\n{'='*50}")
|
||||
# 打印总结
|
||||
logger.info(f"\n{'='*60}")
|
||||
logger.info("所有周期抓取完成!统计:")
|
||||
for period, count in results.items():
|
||||
logger.info(f" {period}: {count} 条")
|
||||
logger.info(f"{'='*50}")
|
||||
logger.info(f" {period}: 新增 {count} 条")
|
||||
logger.info(f"{'='*60}")
|
||||
|
||||
return results
|
||||
|
||||
def get_stats(self):
|
||||
"""获取各周期数据统计"""
|
||||
logger.info(f"\n{'='*60}")
|
||||
logger.info("数据库统计:")
|
||||
logger.info(f"{'='*60}")
|
||||
|
||||
for step, model in self.models.items():
|
||||
suffix = KLINE_CONFIGS.get(step, f'{step}m')
|
||||
try:
|
||||
count = model.select().count()
|
||||
earliest, latest = self.get_db_time_range(step)
|
||||
if earliest and latest:
|
||||
earliest_str = time.strftime('%Y-%m-%d %H:%M', time.localtime(earliest//1000))
|
||||
latest_str = time.strftime('%Y-%m-%d %H:%M', time.localtime(latest//1000))
|
||||
logger.info(f" {suffix:>4}: {count:>8} 条 | {earliest_str} ~ {latest_str}")
|
||||
else:
|
||||
logger.info(f" {suffix:>4}: {count:>8} 条")
|
||||
except Exception as e:
|
||||
logger.error(f" {suffix}: 查询失败 - {e}")
|
||||
|
||||
# 成交记录统计
|
||||
try:
|
||||
trades_count = BitMartETHTrades.select().count()
|
||||
if trades_count > 0:
|
||||
earliest_trade = BitMartETHTrades.select(fn.MIN(BitMartETHTrades.timestamp)).scalar()
|
||||
latest_trade = BitMartETHTrades.select(fn.MAX(BitMartETHTrades.timestamp)).scalar()
|
||||
earliest_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(earliest_trade//1000))
|
||||
latest_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(latest_trade//1000))
|
||||
logger.info(f"trades: {trades_count:>8} 条 | {earliest_str} ~ {latest_str}")
|
||||
else:
|
||||
logger.info(f"trades: {trades_count:>8} 条")
|
||||
except Exception as e:
|
||||
logger.error(f"trades: 查询失败 - {e}")
|
||||
|
||||
# 秒级K线统计
|
||||
try:
|
||||
second_count = BitMartETHSecond.select().count()
|
||||
if second_count > 0:
|
||||
earliest_sec = BitMartETHSecond.select(fn.MIN(BitMartETHSecond.id)).scalar()
|
||||
latest_sec = BitMartETHSecond.select(fn.MAX(BitMartETHSecond.id)).scalar()
|
||||
earliest_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(earliest_sec//1000))
|
||||
latest_str = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(latest_sec//1000))
|
||||
logger.info(f" 1s: {second_count:>8} 条 | {earliest_str} ~ {latest_str}")
|
||||
else:
|
||||
logger.info(f" 1s: {second_count:>8} 条")
|
||||
except Exception as e:
|
||||
logger.error(f" 1s: 查询失败 - {e}")
|
||||
|
||||
logger.info(f"{'='*60}")
|
||||
|
||||
# ==================== 秒级数据相关方法 ====================
|
||||
|
||||
def get_trades(self, limit: int = 100):
|
||||
"""
|
||||
获取最近成交记录
|
||||
:param limit: 获取条数
|
||||
:return: 成交记录列表
|
||||
"""
|
||||
try:
|
||||
response = self.contractAPI.get_trades(
|
||||
contract_symbol=self.contract_symbol,
|
||||
)[0]
|
||||
|
||||
if response['code'] != 1000:
|
||||
logger.error(f"获取成交记录失败: {response}")
|
||||
return []
|
||||
|
||||
trades = response.get('data', {}).get('trades', [])
|
||||
formatted = []
|
||||
for t in trades:
|
||||
formatted.append({
|
||||
'id': int(t.get('trade_id', 0)),
|
||||
'timestamp': int(t.get('create_time', 0)),
|
||||
'price': float(t.get('deal_price', 0)),
|
||||
'volume': float(t.get('deal_vol', 0)),
|
||||
'side': int(t.get('way', 0)),
|
||||
})
|
||||
|
||||
return formatted
|
||||
except Exception as e:
|
||||
logger.error(f"获取成交记录异常: {e}")
|
||||
return []
|
||||
|
||||
def save_trades(self, trades: list):
|
||||
"""保存成交记录到数据库"""
|
||||
new_count = 0
|
||||
for trade in trades:
|
||||
try:
|
||||
_, created = BitMartETHTrades.get_or_create(
|
||||
id=trade['id'],
|
||||
defaults={
|
||||
'timestamp': trade['timestamp'],
|
||||
'price': trade['price'],
|
||||
'volume': trade['volume'],
|
||||
'side': trade['side'],
|
||||
}
|
||||
)
|
||||
if created:
|
||||
new_count += 1
|
||||
except Exception as e:
|
||||
pass # 忽略重复数据
|
||||
return new_count
|
||||
|
||||
def collect_trades_realtime(self, duration_seconds: int = 3600, interval: float = 0.3):
|
||||
"""
|
||||
实时持续采集成交记录(秒级数据源)
|
||||
:param duration_seconds: 采集时长(秒),默认1小时
|
||||
:param interval: 采集间隔(秒),默认0.3秒
|
||||
"""
|
||||
logger.info(f"{'='*60}")
|
||||
logger.info(f"开始实时采集成交记录")
|
||||
logger.info(f"时长: {duration_seconds}秒 ({duration_seconds/3600:.1f}小时)")
|
||||
logger.info(f"间隔: {interval}秒")
|
||||
logger.info(f"{'='*60}")
|
||||
|
||||
start_time = time.time()
|
||||
end_time = start_time + duration_seconds
|
||||
total_saved = 0
|
||||
batch_count = 0
|
||||
|
||||
while time.time() < end_time:
|
||||
trades = self.get_trades(limit=100)
|
||||
if trades:
|
||||
saved = self.save_trades(trades)
|
||||
total_saved += saved
|
||||
batch_count += 1
|
||||
|
||||
# 每10批显示一次进度
|
||||
if batch_count % 10 == 0:
|
||||
elapsed = time.time() - start_time
|
||||
remaining = end_time - time.time()
|
||||
latest = trades[-1]
|
||||
ts_str = datetime.datetime.fromtimestamp(
|
||||
latest['timestamp']/1000
|
||||
).strftime('%H:%M:%S')
|
||||
logger.info(f"[{ts_str}] 价格: {latest['price']:.2f} | "
|
||||
f"本批新增: {saved} | 累计: {total_saved} | "
|
||||
f"剩余: {remaining/60:.1f}分钟")
|
||||
|
||||
time.sleep(interval)
|
||||
|
||||
logger.success(f"采集完成!共新增 {total_saved} 条成交记录")
|
||||
|
||||
# 自动聚合为秒级K线
|
||||
logger.info("正在将成交记录聚合为秒级K线...")
|
||||
self.aggregate_trades_to_seconds()
|
||||
|
||||
return total_saved
|
||||
|
||||
def aggregate_trades_to_seconds(self, start_ts: int = None, end_ts: int = None):
|
||||
"""
|
||||
将成交记录聚合为秒级K线数据
|
||||
:param start_ts: 开始时间戳(毫秒),默认全部
|
||||
:param end_ts: 结束时间戳(毫秒),默认全部
|
||||
:return: 聚合的秒级K线数量
|
||||
"""
|
||||
# 构建查询
|
||||
query = BitMartETHTrades.select().order_by(BitMartETHTrades.timestamp)
|
||||
if start_ts:
|
||||
query = query.where(BitMartETHTrades.timestamp >= start_ts)
|
||||
if end_ts:
|
||||
query = query.where(BitMartETHTrades.timestamp <= end_ts)
|
||||
|
||||
# 按秒聚合
|
||||
second_data = {}
|
||||
trade_count = 0
|
||||
|
||||
for trade in query:
|
||||
trade_count += 1
|
||||
# 取整到秒(毫秒时间戳)
|
||||
second_ts = (trade.timestamp // 1000) * 1000
|
||||
|
||||
if second_ts not in second_data:
|
||||
second_data[second_ts] = {
|
||||
'open': trade.price,
|
||||
'high': trade.price,
|
||||
'low': trade.price,
|
||||
'close': trade.price,
|
||||
'volume': trade.volume,
|
||||
'trade_count': 1
|
||||
}
|
||||
else:
|
||||
second_data[second_ts]['high'] = max(second_data[second_ts]['high'], trade.price)
|
||||
second_data[second_ts]['low'] = min(second_data[second_ts]['low'], trade.price)
|
||||
second_data[second_ts]['close'] = trade.price
|
||||
second_data[second_ts]['volume'] += trade.volume
|
||||
second_data[second_ts]['trade_count'] += 1
|
||||
|
||||
# 保存到数据库
|
||||
saved_count = 0
|
||||
for ts, ohlc in second_data.items():
|
||||
try:
|
||||
BitMartETHSecond.insert(
|
||||
id=ts,
|
||||
open=ohlc['open'],
|
||||
high=ohlc['high'],
|
||||
low=ohlc['low'],
|
||||
close=ohlc['close'],
|
||||
volume=ohlc['volume'],
|
||||
trade_count=ohlc['trade_count']
|
||||
).on_conflict(
|
||||
conflict_target=[BitMartETHSecond.id],
|
||||
update={
|
||||
BitMartETHSecond.open: ohlc['open'],
|
||||
BitMartETHSecond.high: ohlc['high'],
|
||||
BitMartETHSecond.low: ohlc['low'],
|
||||
BitMartETHSecond.close: ohlc['close'],
|
||||
BitMartETHSecond.volume: ohlc['volume'],
|
||||
BitMartETHSecond.trade_count: ohlc['trade_count'],
|
||||
}
|
||||
).execute()
|
||||
saved_count += 1
|
||||
except Exception as e:
|
||||
logger.error(f"保存秒级K线失败 {ts}: {e}")
|
||||
|
||||
logger.success(f"聚合完成!{trade_count} 条成交记录 → {saved_count} 条秒级K线")
|
||||
return saved_count
|
||||
|
||||
def get_second_klines(self, start_ts: int = None, end_ts: int = None):
|
||||
"""
|
||||
获取秒级K线数据
|
||||
:param start_ts: 开始时间戳(毫秒)
|
||||
:param end_ts: 结束时间戳(毫秒)
|
||||
:return: 秒级K线列表
|
||||
"""
|
||||
query = BitMartETHSecond.select().order_by(BitMartETHSecond.id)
|
||||
if start_ts:
|
||||
query = query.where(BitMartETHSecond.id >= start_ts)
|
||||
if end_ts:
|
||||
query = query.where(BitMartETHSecond.id <= end_ts)
|
||||
|
||||
return [{
|
||||
'timestamp': k.id,
|
||||
'open': k.open,
|
||||
'high': k.high,
|
||||
'low': k.low,
|
||||
'close': k.close,
|
||||
'volume': k.volume,
|
||||
'trade_count': k.trade_count
|
||||
} for k in query]
|
||||
|
||||
def aggregate_trades_custom(self, interval_ms: int = 100, start_ts: int = None, end_ts: int = None):
|
||||
"""
|
||||
将成交记录聚合为自定义毫秒级K线数据(不保存到数据库,直接返回)
|
||||
:param interval_ms: 聚合周期(毫秒),如 100=100ms, 500=500ms, 1000=1秒
|
||||
:param start_ts: 开始时间戳(毫秒)
|
||||
:param end_ts: 结束时间戳(毫秒)
|
||||
:return: K线列表 [{'timestamp', 'open', 'high', 'low', 'close', 'volume', 'trade_count'}, ...]
|
||||
"""
|
||||
# 构建查询
|
||||
query = BitMartETHTrades.select().order_by(BitMartETHTrades.timestamp)
|
||||
if start_ts:
|
||||
query = query.where(BitMartETHTrades.timestamp >= start_ts)
|
||||
if end_ts:
|
||||
query = query.where(BitMartETHTrades.timestamp <= end_ts)
|
||||
|
||||
# 按指定间隔聚合
|
||||
interval_data = {}
|
||||
trade_count = 0
|
||||
|
||||
for trade in query:
|
||||
trade_count += 1
|
||||
# 取整到指定间隔
|
||||
interval_ts = (trade.timestamp // interval_ms) * interval_ms
|
||||
|
||||
if interval_ts not in interval_data:
|
||||
interval_data[interval_ts] = {
|
||||
'open': trade.price,
|
||||
'high': trade.price,
|
||||
'low': trade.price,
|
||||
'close': trade.price,
|
||||
'volume': trade.volume,
|
||||
'trade_count': 1
|
||||
}
|
||||
else:
|
||||
interval_data[interval_ts]['high'] = max(interval_data[interval_ts]['high'], trade.price)
|
||||
interval_data[interval_ts]['low'] = min(interval_data[interval_ts]['low'], trade.price)
|
||||
interval_data[interval_ts]['close'] = trade.price
|
||||
interval_data[interval_ts]['volume'] += trade.volume
|
||||
interval_data[interval_ts]['trade_count'] += 1
|
||||
|
||||
# 转换为列表
|
||||
result = []
|
||||
for ts, ohlc in sorted(interval_data.items()):
|
||||
result.append({
|
||||
'timestamp': ts,
|
||||
'datetime': datetime.datetime.fromtimestamp(ts/1000).strftime('%Y-%m-%d %H:%M:%S.%f')[:-3],
|
||||
'open': ohlc['open'],
|
||||
'high': ohlc['high'],
|
||||
'low': ohlc['low'],
|
||||
'close': ohlc['close'],
|
||||
'volume': ohlc['volume'],
|
||||
'trade_count': ohlc['trade_count']
|
||||
})
|
||||
|
||||
logger.info(f"聚合完成: {trade_count} 条成交记录 → {len(result)} 条 {interval_ms}ms K线")
|
||||
return result
|
||||
|
||||
def get_raw_trades(self, start_ts: int = None, end_ts: int = None, limit: int = None):
|
||||
"""
|
||||
获取原始成交记录(逐笔数据,毫秒级)
|
||||
:param start_ts: 开始时间戳(毫秒)
|
||||
:param end_ts: 结束时间戳(毫秒)
|
||||
:param limit: 最大返回条数
|
||||
:return: 成交记录列表
|
||||
"""
|
||||
query = BitMartETHTrades.select().order_by(BitMartETHTrades.timestamp)
|
||||
if start_ts:
|
||||
query = query.where(BitMartETHTrades.timestamp >= start_ts)
|
||||
if end_ts:
|
||||
query = query.where(BitMartETHTrades.timestamp <= end_ts)
|
||||
if limit:
|
||||
query = query.limit(limit)
|
||||
|
||||
return [{
|
||||
'id': t.id,
|
||||
'timestamp': t.timestamp,
|
||||
'datetime': datetime.datetime.fromtimestamp(t.timestamp/1000).strftime('%Y-%m-%d %H:%M:%S.%f')[:-3],
|
||||
'price': t.price,
|
||||
'volume': t.volume,
|
||||
'side': '买' if t.side == 1 else '卖'
|
||||
} for t in query]
|
||||
|
||||
def close(self):
|
||||
"""关闭数据库连接"""
|
||||
if not db.is_closed():
|
||||
@@ -269,11 +732,30 @@ if __name__ == '__main__':
|
||||
collector = BitMartMultiKlineCollector()
|
||||
|
||||
try:
|
||||
# 抓取尽可能多的历史数据(从现在向前,直到遇到API限制自动停止)
|
||||
# 目标:2025-01-01,但实际能抓取多少取决于 BitMart API 的历史数据限制
|
||||
collector.collect_all_periods(
|
||||
start_date='2025-01-01', # 目标起始日期
|
||||
# 查看当前数据统计
|
||||
collector.get_stats()
|
||||
|
||||
# ============ 选择要执行的任务 ============
|
||||
|
||||
# 任务1: 抓取K线数据(1分钟~1小时周期)
|
||||
# 从 2025-01-01 抓取到当前时间(支持断点续传)
|
||||
collector.collect_from_date(
|
||||
start_date='2025-01-01',
|
||||
periods=[1, 3, 5, 15, 30, 60] # 所有周期
|
||||
)
|
||||
|
||||
# 任务2: 实时采集秒级数据(成交记录)
|
||||
# 注意: 秒级数据只能实时采集,无法获取历史
|
||||
# collector.collect_trades_realtime(
|
||||
# duration_seconds=3600, # 采集1小时
|
||||
# interval=0.3 # 每0.3秒请求一次
|
||||
# )
|
||||
|
||||
# 任务3: 将已采集的成交记录聚合为秒级K线
|
||||
# collector.aggregate_trades_to_seconds()
|
||||
|
||||
# 再次查看统计
|
||||
collector.get_stats()
|
||||
|
||||
finally:
|
||||
collector.close()
|
||||
|
||||
382
bitmart/趋势策略.py
382
bitmart/趋势策略.py
@@ -1,382 +0,0 @@
|
||||
import time
|
||||
import datetime
|
||||
import openBrowser
|
||||
|
||||
from tqdm import tqdm
|
||||
from loguru import logger
|
||||
from bit_tools import openBrowser
|
||||
from DrissionPage import ChromiumPage
|
||||
from DrissionPage import ChromiumOptions
|
||||
|
||||
from bitmart.api_contract import APIContract
|
||||
|
||||
|
||||
class BitmartFuturesTransaction:
|
||||
def __init__(self, bit_id):
|
||||
|
||||
self.page: ChromiumPage | None = None
|
||||
|
||||
self.api_key = "a0fb7b98464fd9bcce67e7c519d58ec10d0c38a8"
|
||||
self.secret_key = "4eaeba78e77aeaab1c2027f846a276d164f264a44c2c1bb1c5f3be50c8de1ca5"
|
||||
self.memo = "合约交易"
|
||||
|
||||
self.contract_symbol = "ETHUSDT"
|
||||
|
||||
self.contractAPI = APIContract(self.api_key, self.secret_key, self.memo, timeout=(5, 15))
|
||||
|
||||
self.start = 0 # 持仓状态: -1 空, 0 无, 1 多
|
||||
self.direction = None
|
||||
|
||||
self.pbar = tqdm(total=30, desc="等待K线", ncols=80)
|
||||
|
||||
self.last_kline_time = None
|
||||
|
||||
self.leverage = "100" # 高杠杆(全仓模式下可开更大仓位)
|
||||
self.open_type = "cross" # 全仓模式(你的“成本开仓”需求)
|
||||
self.risk_percent = 0.01 # 每次开仓使用可用余额的 1%
|
||||
|
||||
self.open_avg_price = None # 开仓价格
|
||||
self.current_amount = None # 持仓量
|
||||
|
||||
self.bit_id = bit_id
|
||||
|
||||
def get_klines(self):
|
||||
"""获取最近3根30分钟K线(step=30)"""
|
||||
try:
|
||||
end_time = int(time.time())
|
||||
# 获取足够多的条目确保有最新3根
|
||||
response = self.contractAPI.get_kline(
|
||||
contract_symbol=self.contract_symbol,
|
||||
step=30, # 30分钟
|
||||
start_time=end_time - 3600 * 10, # 取最近10小时
|
||||
end_time=end_time
|
||||
)[0]["data"]
|
||||
|
||||
# 每根: [timestamp, open, high, low, close, volume]
|
||||
formatted = []
|
||||
for k in response:
|
||||
formatted.append({
|
||||
'id': int(k["timestamp"]),
|
||||
'open': float(k["open_price"]),
|
||||
'high': float(k["high_price"]),
|
||||
'low': float(k["low_price"]),
|
||||
'close': float(k["close_price"])
|
||||
})
|
||||
formatted.sort(key=lambda x: x['id'])
|
||||
return formatted # 最近3根: kline_1 (最老), kline_2, kline_3 (最新)
|
||||
except Exception as e:
|
||||
logger.error(f"获取K线异常: {e}")
|
||||
self.ding(error=True, msg="获取K线异常")
|
||||
return None
|
||||
|
||||
def get_current_price(self):
|
||||
"""获取当前最新价格,用于计算张数"""
|
||||
try:
|
||||
end_time = int(time.time())
|
||||
response = self.contractAPI.get_kline(
|
||||
contract_symbol=self.contract_symbol,
|
||||
step=1, # 1分钟
|
||||
start_time=end_time - 3600 * 3, # 取最近10小时
|
||||
end_time=end_time
|
||||
)[0]
|
||||
if response['code'] == 1000:
|
||||
return float(response['data'][-1]["close_price"])
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"获取价格异常: {e}")
|
||||
return None
|
||||
|
||||
def get_available_balance(self):
|
||||
"""获取合约账户可用USDT余额"""
|
||||
try:
|
||||
response = self.contractAPI.get_assets_detail()[0]
|
||||
if response['code'] == 1000:
|
||||
data = response['data']
|
||||
if isinstance(data, dict):
|
||||
return float(data.get('available_balance', 0))
|
||||
elif isinstance(data, list):
|
||||
for asset in data:
|
||||
if asset.get('currency') == 'USDT':
|
||||
return float(asset.get('available_balance', 0))
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"余额查询异常: {e}")
|
||||
return None
|
||||
|
||||
# 获取当前持仓方向
|
||||
def get_position_status(self):
|
||||
"""获取当前持仓方向"""
|
||||
try:
|
||||
response = self.contractAPI.get_position(contract_symbol=self.contract_symbol)[0]
|
||||
if response['code'] == 1000:
|
||||
positions = response['data']
|
||||
if not positions:
|
||||
self.start = 0
|
||||
return True
|
||||
self.start = 1 if positions[0]['position_type'] == 1 else -1
|
||||
self.open_avg_price = positions[0]['open_avg_price']
|
||||
self.current_amount = positions[0]['current_amount']
|
||||
self.position_cross = positions[0]["position_cross"]
|
||||
return True
|
||||
|
||||
else:
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"持仓查询异常: {e}")
|
||||
return False
|
||||
|
||||
# 设置杠杆和全仓
|
||||
def set_leverage(self):
|
||||
"""程序启动时设置全仓 + 高杠杆"""
|
||||
try:
|
||||
response = self.contractAPI.post_submit_leverage(
|
||||
contract_symbol=self.contract_symbol,
|
||||
leverage=self.leverage,
|
||||
open_type=self.open_type
|
||||
)[0]
|
||||
if response['code'] == 1000:
|
||||
logger.success(f"全仓模式 + {self.leverage}x 杠杆设置成功")
|
||||
return True
|
||||
else:
|
||||
logger.error(f"杠杆设置失败: {response}")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"设置杠杆异常: {e}")
|
||||
return False
|
||||
|
||||
def openBrowser(self):
|
||||
"""打开 TGE 对应浏览器实例"""
|
||||
try:
|
||||
bit_port = openBrowser(id=self.bit_id)
|
||||
co = ChromiumOptions()
|
||||
co.set_local_port(port=bit_port)
|
||||
self.page = ChromiumPage(addr_or_opts=co)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def take_over_browser(self):
|
||||
"""接管浏览器"""
|
||||
try:
|
||||
co = ChromiumOptions()
|
||||
co.set_local_port(self.tge_port)
|
||||
self.page = ChromiumPage(addr_or_opts=co)
|
||||
self.page.set.window.max()
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def close_extra_tabs(self):
|
||||
"""关闭多余 tab"""
|
||||
try:
|
||||
for idx, tab in enumerate(self.page.get_tabs()):
|
||||
if idx > 0:
|
||||
tab.close()
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def click_safe(self, xpath, sleep=0.5):
|
||||
"""安全点击"""
|
||||
try:
|
||||
ele = self.page.ele(xpath)
|
||||
if not ele:
|
||||
return False
|
||||
ele.scroll.to_see(center=True)
|
||||
time.sleep(sleep)
|
||||
ele.click()
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def 平仓(self):
|
||||
self.click_safe('x://span[normalize-space(text()) ="市价"]')
|
||||
|
||||
def 开单(self, marketPriceLongOrder=0, limitPriceShortOrder=0, size=None, price=None):
|
||||
"""
|
||||
marketPriceLongOrder 市价最多或者做空,1是做多,-1是做空
|
||||
limitPriceShortOrder 限价最多或者做空
|
||||
"""
|
||||
|
||||
if marketPriceLongOrder == -1:
|
||||
self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
self.page.ele('x://*[@id="size_0"]').input(size)
|
||||
self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]')
|
||||
elif marketPriceLongOrder == 1:
|
||||
self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
self.page.ele('x://*[@id="size_0"]').input(size)
|
||||
self.click_safe('x://span[normalize-space(text()) ="买入/做多"]')
|
||||
|
||||
if limitPriceShortOrder == -1:
|
||||
self.click_safe('x://button[normalize-space(text()) ="限价"]')
|
||||
self.page.ele('x://*[@id="price_0"]').input(vals=price, clear=True)
|
||||
time.sleep(1)
|
||||
self.page.ele('x://*[@id="size_0"]').input(1)
|
||||
self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]')
|
||||
elif limitPriceShortOrder == 1:
|
||||
self.click_safe('x://button[normalize-space(text()) ="限价"]')
|
||||
self.page.ele('x://*[@id="price_0"]').input(vals=price, clear=True)
|
||||
time.sleep(1)
|
||||
self.page.ele('x://*[@id="size_0"]').input(1)
|
||||
self.click_safe('x://span[normalize-space(text()) ="买入/做多"]')
|
||||
|
||||
def ding(self, text, error=False):
|
||||
logger.info(text)
|
||||
|
||||
def close_extra_tabs_in_browser(self):
|
||||
|
||||
try:
|
||||
for _, i in enumerate(self.page.get_tabs()):
|
||||
if _ == 0:
|
||||
continue
|
||||
|
||||
i.close()
|
||||
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
def get_now_time(self):
|
||||
# 获取当前时间戳
|
||||
current_timestamp = time.time()
|
||||
# 将当前时间戳转换为 datetime 对象
|
||||
current_datetime = datetime.datetime.fromtimestamp(current_timestamp)
|
||||
|
||||
# 计算距离当前时间最近的整点或 30 分时刻
|
||||
if current_datetime.minute < 30:
|
||||
target_datetime = current_datetime.replace(minute=0, second=0, microsecond=0)
|
||||
else:
|
||||
target_datetime = current_datetime.replace(minute=30, second=0, microsecond=0)
|
||||
|
||||
# 将目标 datetime 对象转换为时间戳
|
||||
target_timestamp = target_datetime.timestamp()
|
||||
|
||||
return int(target_timestamp)
|
||||
|
||||
def is_bullish(self, c): # 阳线
|
||||
return float(c['close']) > float(c['open'])
|
||||
|
||||
def is_bearish(self, c): # 阴线
|
||||
return float(c['close']) < float(c['open'])
|
||||
|
||||
def check_signal(self, prev, curr):
|
||||
"""
|
||||
包住形态信号判定(优化版):
|
||||
只看两种信号,严格按照收盘价与开盘价的比较:
|
||||
|
||||
1. 阳包阴(涨包跌,前跌后涨)-> 做多:
|
||||
- 前一根是跌(阴线:close < open)
|
||||
- 后一根是涨(阳线:close > open)
|
||||
- 且:涨的收盘价 > 跌的开盘价(curr['close'] > prev['open'])
|
||||
|
||||
2. 阴包阳(跌包涨,前涨后跌)-> 做空:
|
||||
- 前一根是涨(阳线:close > open)
|
||||
- 后一根是跌(阴线:close < open)
|
||||
- 且:跌的收盘价 < 涨的开盘价(curr['close'] < prev['open'])
|
||||
"""
|
||||
p_open = float(prev['open'])
|
||||
c_close = float(curr['close'])
|
||||
|
||||
# 阳包阴(涨包跌,前跌后涨) -> 做多:涨的收盘价 > 跌的开盘价
|
||||
if self.is_bearish(prev) and self.is_bullish(curr) and c_close > p_open:
|
||||
return "long", "bear_bull_engulf"
|
||||
|
||||
# 阴包阳(跌包涨,前涨后跌) -> 做空:跌的收盘价 < 涨的开盘价
|
||||
if self.is_bullish(prev) and self.is_bearish(curr) and c_close < p_open:
|
||||
return "short", "bull_bear_engulf"
|
||||
|
||||
return None, None
|
||||
|
||||
def action(self):
|
||||
# 启动时设置全仓高杠杆
|
||||
if not self.set_leverage():
|
||||
logger.error("杠杆设置失败,程序继续运行但可能下单失败")
|
||||
return
|
||||
|
||||
# 1. 打开浏览器
|
||||
if not self.openBrowser():
|
||||
self.ding("打开 TGE 失败!", error=True)
|
||||
return
|
||||
logger.info("TGE 端口获取成功")
|
||||
|
||||
if self.close_extra_tabs_in_browser():
|
||||
logger.info('关闭多余标签页成功!!!')
|
||||
else:
|
||||
logger.info('关闭多余标签页失败!!!')
|
||||
|
||||
self.page.get("https://derivatives.bitmart.com/zh-CN/futures/ETHUSDT")
|
||||
|
||||
self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
|
||||
self.pbar = tqdm(total=30, desc="等待时间中", ncols=80) # desc:进度条说明,ncols:长度
|
||||
|
||||
self.time_start = None # 时间状态 避免同一个时段,发生太多消息
|
||||
while True:
|
||||
|
||||
# 获取当前时间
|
||||
current_time = time.localtime()
|
||||
current_minute = current_time.tm_min
|
||||
|
||||
if current_minute < 30:
|
||||
self.pbar.n = current_minute
|
||||
self.pbar.refresh()
|
||||
else:
|
||||
self.pbar.n = current_minute - 30
|
||||
self.pbar.refresh()
|
||||
|
||||
if self.time_start == self.get_now_time():
|
||||
time.sleep(5)
|
||||
continue
|
||||
|
||||
new_price_datas = self.get_klines()
|
||||
if not new_price_datas:
|
||||
logger.info("获取最新价格有问题!!!")
|
||||
|
||||
new_price_datas1 = sorted(new_price_datas, key=lambda x: x["id"])
|
||||
self.kline_1, self.kline_2, self.kline_3 = new_price_datas1[-3:]
|
||||
|
||||
# 判断抓取的数据是否正确
|
||||
if self.get_now_time() != self.kline_3["id"]:
|
||||
continue
|
||||
|
||||
self.time_start = self.get_now_time()
|
||||
|
||||
if self.get_position_status():
|
||||
logger.info("获取仓位信息成功!!!")
|
||||
else:
|
||||
logger.info("获取仓位信息失败!!!")
|
||||
|
||||
self.send_dingtalk_message(message_content=f"获取仓位信息失败!!!", type=0)
|
||||
continue
|
||||
|
||||
if self.start == 1:
|
||||
if self.is_bearish(self.kline_1) and self.is_bearish(self.kline_2):
|
||||
self.平仓()
|
||||
elif self.start == -1:
|
||||
if self.is_bullish(self.kline_1) and self.is_bullish(self.kline_2):
|
||||
self.平仓()
|
||||
|
||||
self.direction, signal_key = self.check_signal(prev=self.kline_1, curr=self.kline_2) # 判断信号
|
||||
|
||||
if self.direction == "long":
|
||||
if self.start == -1:
|
||||
self.平仓()
|
||||
self.开单(marketPriceLongOrder=1, size=self.get_available_balance() * self.risk_percent)
|
||||
elif self.start == 0:
|
||||
self.开单(marketPriceLongOrder=1, size=self.get_available_balance() * self.risk_percent)
|
||||
|
||||
if self.direction == "short":
|
||||
if self.start == 1:
|
||||
self.平仓()
|
||||
self.开单(marketPriceLongOrder=-1, size=self.get_available_balance() * self.risk_percent)
|
||||
elif self.start == 0:
|
||||
self.开单(marketPriceLongOrder=-1, size=self.get_available_balance() * self.risk_percent)
|
||||
|
||||
self.pbar.reset() # 重置进度条
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
BitmartFuturesTransaction(bit_id="f2320f57e24c45529a009e1541e25961").action()
|
||||
@@ -1,21 +0,0 @@
|
||||
2025-12-20 02:51:09.247 | WARNING | __main__:__init__:28 - 请确认Memo是否正确!默认Memo可能导致签名失败
|
||||
2025-12-20 02:51:09.252 | INFO | __main__:init_api_client:57 - 尝试初始化API客户端 (尝试 1/3)
|
||||
2025-12-20 02:51:09.253 | ERROR | __main__:test_api_connection:112 - API连接测试失败: APIContract.get_depth() got an unexpected keyword argument 'limit'
|
||||
2025-12-20 02:51:09.254 | SUCCESS | __main__:init_api_client:87 - API客户端初始化成功
|
||||
2025-12-20 02:51:09.254 | INFO | __main__:action:220 - 启动BitMart合约策略...
|
||||
2025-12-20 02:51:09.254 | INFO | __main__:set_leverage_with_retry:121 - 尝试设置杠杆 (尝试 1/3)
|
||||
2025-12-20 02:51:09.814 | ERROR | __main__:set_leverage_with_retry:149 - API异常: APIException(http status=401): response={"code":30005,"msg":"Header X-BM-SIGN is wrong","trace":"d9c107e3-7564-474e-9eaf-59667f869ae5"}
|
||||
|
||||
2025-12-20 02:51:09.814 | INFO | __main__:set_leverage_with_retry:161 - 等待1秒后重试...
|
||||
2025-12-20 02:51:10.815 | INFO | __main__:set_leverage_with_retry:121 - 尝试设置杠杆 (尝试 2/3)
|
||||
2025-12-20 02:51:10.923 | ERROR | __main__:set_leverage_with_retry:149 - API异常: APIException(http status=401): response={"trace":"a7fa76a2-2e34-435b-acbe-90e37b47fe48","code":30005,"msg":"Header X-BM-SIGN is wrong"}
|
||||
|
||||
2025-12-20 02:51:10.923 | INFO | __main__:set_leverage_with_retry:161 - 等待2秒后重试...
|
||||
2025-12-20 02:51:12.923 | INFO | __main__:set_leverage_with_retry:121 - 尝试设置杠杆 (尝试 3/3)
|
||||
2025-12-20 02:51:13.029 | ERROR | __main__:set_leverage_with_retry:149 - API异常: APIException(http status=401): response={"code":30005,"msg":"Header X-BM-SIGN is wrong","trace":"97b6c791-5812-4285-a659-dbba3f6170e6"}
|
||||
|
||||
2025-12-20 02:51:13.029 | WARNING | __main__:set_leverage_with_retry:168 - 杠杆设置失败,但程序将继续运行
|
||||
2025-12-20 02:51:13.030 | WARNING | __main__:action:226 - 杠杆设置失败,使用系统默认杠杆继续运行
|
||||
2025-12-20 02:51:13.150 | SUCCESS | __main__:action:254 - 获取到新K线: 2025-12-20 02:30:00
|
||||
2025-12-20 02:51:13.150 | INFO | __main__:action:259 - 当前价格: 2959.49
|
||||
2025-12-20 02:51:13.311 | INFO | __main__:action:268 - 用户中断程序
|
||||
@@ -1,46 +0,0 @@
|
||||
2025-12-20 03:08:42.274 | INFO | __main__:initialize_api:66 - 尝试API配置 1/3
|
||||
2025-12-20 03:08:42.275 | INFO | __main__:initialize_api:67 - Memo: ''
|
||||
2025-12-20 03:08:42.275 | ERROR | __main__:test_api_connection:108 - API连接测试失败: 'APIContract' object has no attribute 'get_contracts'
|
||||
2025-12-20 03:08:42.276 | WARNING | __main__:initialize_api:84 - API配置 1 连接失败,尝试下一个配置
|
||||
2025-12-20 03:08:42.276 | INFO | __main__:initialize_api:66 - 尝试API配置 2/3
|
||||
2025-12-20 03:08:42.276 | INFO | __main__:initialize_api:67 - Memo: '合约交易'
|
||||
2025-12-20 03:08:42.276 | ERROR | __main__:test_api_connection:108 - API连接测试失败: 'APIContract' object has no attribute 'get_contracts'
|
||||
2025-12-20 03:08:42.276 | WARNING | __main__:initialize_api:84 - API配置 2 连接失败,尝试下一个配置
|
||||
2025-12-20 03:08:42.276 | INFO | __main__:initialize_api:66 - 尝试API配置 3/3
|
||||
2025-12-20 03:08:42.277 | INFO | __main__:initialize_api:67 - Memo: 'None'
|
||||
2025-12-20 03:08:42.277 | ERROR | __main__:test_api_connection:108 - API连接测试失败: 'APIContract' object has no attribute 'get_contracts'
|
||||
2025-12-20 03:08:42.277 | WARNING | __main__:initialize_api:84 - API配置 3 连接失败,尝试下一个配置
|
||||
2025-12-20 03:08:42.277 | ERROR | __main__:initialize_api:89 - 所有API配置都失败
|
||||
2025-12-20 03:08:42.281 | ERROR | __main__:action:408 - API初始化失败,程序无法运行
|
||||
2025-12-20 03:08:48.311 | INFO | __main__:initialize_api:66 - 尝试API配置 1/3
|
||||
2025-12-20 03:08:48.312 | INFO | __main__:initialize_api:67 - Memo: ''
|
||||
2025-12-20 03:08:48.313 | ERROR | __main__:test_api_connection:108 - API连接测试失败: 'APIContract' object has no attribute 'get_contracts'
|
||||
2025-12-20 03:08:48.313 | WARNING | __main__:initialize_api:84 - API配置 1 连接失败,尝试下一个配置
|
||||
2025-12-20 03:08:48.313 | INFO | __main__:initialize_api:66 - 尝试API配置 2/3
|
||||
2025-12-20 03:08:48.313 | INFO | __main__:initialize_api:67 - Memo: '合约交易'
|
||||
2025-12-20 03:08:48.313 | ERROR | __main__:test_api_connection:108 - API连接测试失败: 'APIContract' object has no attribute 'get_contracts'
|
||||
2025-12-20 03:08:48.313 | WARNING | __main__:initialize_api:84 - API配置 2 连接失败,尝试下一个配置
|
||||
2025-12-20 03:08:48.313 | INFO | __main__:initialize_api:66 - 尝试API配置 3/3
|
||||
2025-12-20 03:08:48.313 | INFO | __main__:initialize_api:67 - Memo: 'None'
|
||||
2025-12-20 03:08:48.313 | ERROR | __main__:test_api_connection:108 - API连接测试失败: 'APIContract' object has no attribute 'get_contracts'
|
||||
2025-12-20 03:08:48.313 | WARNING | __main__:initialize_api:84 - API配置 3 连接失败,尝试下一个配置
|
||||
2025-12-20 03:08:48.314 | ERROR | __main__:initialize_api:89 - 所有API配置都失败
|
||||
2025-12-20 03:08:48.317 | ERROR | __main__:action:408 - API初始化失败,程序无法运行
|
||||
2025-12-20 03:09:25.953 | INFO | __main__:initialize_api:66 - 尝试API配置 1/3
|
||||
2025-12-20 03:09:25.954 | INFO | __main__:initialize_api:67 - Memo: ''
|
||||
2025-12-20 03:09:25.954 | ERROR | __main__:test_api_connection:108 - API连接测试失败: 'APIContract' object has no attribute 'get_contracts'
|
||||
2025-12-20 03:09:25.954 | WARNING | __main__:initialize_api:84 - API配置 1 连接失败,尝试下一个配置
|
||||
2025-12-20 03:09:25.954 | INFO | __main__:initialize_api:66 - 尝试API配置 2/3
|
||||
2025-12-20 03:09:25.954 | INFO | __main__:initialize_api:67 - Memo: '合约交易'
|
||||
2025-12-20 03:09:25.954 | ERROR | __main__:test_api_connection:108 - API连接测试失败: 'APIContract' object has no attribute 'get_contracts'
|
||||
2025-12-20 03:09:25.954 | WARNING | __main__:initialize_api:84 - API配置 2 连接失败,尝试下一个配置
|
||||
2025-12-20 03:09:25.954 | INFO | __main__:initialize_api:66 - 尝试API配置 3/3
|
||||
2025-12-20 03:09:25.954 | INFO | __main__:initialize_api:67 - Memo: 'None'
|
||||
2025-12-20 03:09:25.955 | ERROR | __main__:test_api_connection:108 - API连接测试失败: 'APIContract' object has no attribute 'get_contracts'
|
||||
2025-12-20 03:09:25.955 | WARNING | __main__:initialize_api:84 - API配置 3 连接失败,尝试下一个配置
|
||||
2025-12-20 03:09:25.955 | ERROR | __main__:initialize_api:89 - 所有API配置都失败
|
||||
2025-12-20 03:09:25.958 | ERROR | __main__:action:408 - API初始化失败,程序无法运行
|
||||
2025-12-20 03:12:23.884 | INFO | __main__:action:396 - 启动BitMart合约交易策略...
|
||||
2025-12-20 03:12:23.885 | INFO | __main__:test_api_connection:48 - 测试API连接...
|
||||
2025-12-20 03:12:24.359 | ERROR | __main__:test_api_connection:90 - API连接测试异常: 'tuple' object has no attribute 'get'
|
||||
2025-12-20 03:12:24.359 | ERROR | __main__:action:400 - API连接测试失败
|
||||
415
evm_助记词查询余额.py
415
evm_助记词查询余额.py
@@ -1,415 +0,0 @@
|
||||
"""
|
||||
EVM助记词碰撞工具 - 高速版本
|
||||
批量生成助记词并检查余额,发现有钱包自动保存
|
||||
|
||||
依赖安装:
|
||||
pip install mnemonic eth-account web3 loguru
|
||||
|
||||
使用方法:
|
||||
python evm_助记词查询余额.py
|
||||
"""
|
||||
|
||||
import os
|
||||
import json
|
||||
import threading
|
||||
from datetime import datetime
|
||||
from mnemonic import Mnemonic
|
||||
from eth_account import Account
|
||||
from web3 import Web3
|
||||
from loguru import logger
|
||||
import time
|
||||
|
||||
# 尝试导入POA中间件
|
||||
try:
|
||||
from web3.middleware import geth_poa_middleware
|
||||
except ImportError:
|
||||
try:
|
||||
from web3.middleware.geth_poa import geth_poa_middleware
|
||||
except ImportError:
|
||||
geth_poa_middleware = None
|
||||
|
||||
# 配置日志
|
||||
logger.remove()
|
||||
logger.add(lambda msg: print(msg, end=''), format="<green>{time:HH:mm:ss}</green> | <level>{level: <8}</level> | {message}", level="INFO")
|
||||
|
||||
# 全局统计
|
||||
stats = {
|
||||
'total_checked': 0,
|
||||
'found_balance': 0,
|
||||
'start_time': time.time(),
|
||||
'lock': threading.Lock()
|
||||
}
|
||||
|
||||
# 结果保存文件
|
||||
RESULTS_FILE = "found_wallets.txt"
|
||||
SAVE_LOCK = threading.Lock()
|
||||
|
||||
|
||||
class FastEVMWallet:
|
||||
"""高速EVM钱包碰撞工具"""
|
||||
|
||||
# 优化的RPC端点(使用多个备用节点提高速度)
|
||||
RPC_ENDPOINTS = {
|
||||
"ethereum": [
|
||||
"https://eth.llamarpc.com",
|
||||
"https://rpc.ankr.com/eth",
|
||||
"https://ethereum.publicnode.com",
|
||||
],
|
||||
"bsc": [
|
||||
"https://bsc-dataseed1.binance.org",
|
||||
"https://bsc-dataseed2.binance.org",
|
||||
"https://rpc.ankr.com/bsc",
|
||||
],
|
||||
# "polygon": [
|
||||
# "https://polygon-rpc.com",
|
||||
# "https://rpc.ankr.com/polygon",
|
||||
# ],
|
||||
# "arbitrum": [
|
||||
# "https://arb1.arbitrum.io/rpc",
|
||||
# "https://rpc.ankr.com/arbitrum",
|
||||
# ],
|
||||
# "optimism": [
|
||||
# "https://mainnet.optimism.io",
|
||||
# "https://rpc.ankr.com/optimism",
|
||||
# ],
|
||||
"base": [
|
||||
"https://mainnet.base.org",
|
||||
"https://rpc.ankr.com/base",
|
||||
],
|
||||
}
|
||||
|
||||
def __init__(self, chains=None):
|
||||
"""
|
||||
初始化
|
||||
|
||||
Args:
|
||||
chains: 要查询的链列表,None表示查询所有链
|
||||
"""
|
||||
self.mnemo = Mnemonic("english")
|
||||
Account.enable_unaudited_hdwallet_features()
|
||||
|
||||
# 选择要查询的链(默认只查询主要链以提高速度)
|
||||
if chains is None:
|
||||
self.chains = ["ethereum", "bsc", "polygon", "arbitrum", "base"]
|
||||
else:
|
||||
self.chains = chains
|
||||
|
||||
# 为每个链创建Web3连接池
|
||||
self.w3_pools = {}
|
||||
self._init_connections()
|
||||
|
||||
def _init_connections(self):
|
||||
"""初始化RPC连接"""
|
||||
logger.info("正在初始化RPC连接...")
|
||||
for chain in self.chains:
|
||||
if chain in self.RPC_ENDPOINTS:
|
||||
# 尝试所有RPC节点,直到找到一个可用的
|
||||
w3 = None
|
||||
for rpc_url in self.RPC_ENDPOINTS[chain]:
|
||||
try:
|
||||
# 设置更短的超时时间,避免阻塞
|
||||
w3 = Web3(Web3.HTTPProvider(rpc_url, request_kwargs={'timeout': 3}))
|
||||
|
||||
# 某些链需要POA中间件
|
||||
if chain in ["bsc", "polygon"] and geth_poa_middleware is not None:
|
||||
try:
|
||||
w3.middleware_onion.inject(geth_poa_middleware, layer=0)
|
||||
except:
|
||||
pass
|
||||
|
||||
# 测试连接(快速测试)
|
||||
try:
|
||||
if w3.is_connected():
|
||||
self.w3_pools[chain] = w3
|
||||
logger.info(f"✓ {chain} 连接成功: {rpc_url}")
|
||||
break
|
||||
except:
|
||||
continue
|
||||
except Exception as e:
|
||||
continue
|
||||
|
||||
if chain not in self.w3_pools:
|
||||
logger.warning(f"✗ {chain} 连接失败,将跳过该链")
|
||||
|
||||
def generate_mnemonic(self, strength=128):
|
||||
"""快速生成助记词"""
|
||||
return self.mnemo.generate(strength=strength)
|
||||
|
||||
def mnemonic_to_address(self, mnemonic, account_index=0):
|
||||
"""
|
||||
从助记词快速生成地址(不生成私钥,节省时间)
|
||||
|
||||
Returns:
|
||||
str: 钱包地址,失败返回None
|
||||
"""
|
||||
try:
|
||||
account = Account.from_mnemonic(mnemonic, account_path=f"m/44'/60'/0'/0/{account_index}")
|
||||
return account.address
|
||||
except:
|
||||
return None
|
||||
|
||||
def quick_check_balance(self, address):
|
||||
"""
|
||||
快速检查地址是否有余额(只检查主要链,一旦发现余额就返回)
|
||||
|
||||
Returns:
|
||||
dict: 如果有余额返回余额信息,否则返回None
|
||||
"""
|
||||
# 只检查最快的链(ethereum和bsc),提高速度
|
||||
priority_chains = ["ethereum", "bsc"]
|
||||
|
||||
for chain in priority_chains:
|
||||
if chain not in self.chains:
|
||||
continue
|
||||
try:
|
||||
w3 = self.w3_pools.get(chain)
|
||||
if not w3:
|
||||
continue
|
||||
|
||||
# 查询余额(设置超时,避免阻塞)
|
||||
try:
|
||||
balance_wei = w3.eth.get_balance(address)
|
||||
|
||||
if balance_wei > 0:
|
||||
balance_eth = Web3.from_wei(balance_wei, 'ether')
|
||||
return {
|
||||
'chain': chain,
|
||||
'address': address,
|
||||
'balance_wei': balance_wei,
|
||||
'balance_eth': float(balance_eth),
|
||||
'balance_formatted': f"{balance_eth:.6f} ETH"
|
||||
}
|
||||
except Exception as e:
|
||||
# 查询失败,尝试下一个链
|
||||
continue
|
||||
except:
|
||||
continue
|
||||
|
||||
return None
|
||||
|
||||
def check_all_chains(self, address):
|
||||
"""
|
||||
检查所有链的余额(发现余额后调用)
|
||||
|
||||
Returns:
|
||||
list: 所有有余额的链信息
|
||||
"""
|
||||
results = []
|
||||
for chain in self.chains:
|
||||
try:
|
||||
w3 = self.w3_pools.get(chain)
|
||||
if not w3 or not w3.is_connected():
|
||||
continue
|
||||
|
||||
balance_wei = w3.eth.get_balance(address)
|
||||
if balance_wei > 0:
|
||||
balance_eth = Web3.from_wei(balance_wei, 'ether')
|
||||
results.append({
|
||||
'chain': chain,
|
||||
'balance_wei': balance_wei,
|
||||
'balance_eth': float(balance_eth),
|
||||
'balance_formatted': f"{balance_eth:.6f} ETH"
|
||||
})
|
||||
except:
|
||||
continue
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def save_found_wallet(mnemonic, address, private_key, balances):
|
||||
"""保存发现的钱包信息"""
|
||||
with SAVE_LOCK:
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
||||
info = {
|
||||
'timestamp': timestamp,
|
||||
'mnemonic': mnemonic,
|
||||
'address': address,
|
||||
'private_key': private_key,
|
||||
'balances': balances
|
||||
}
|
||||
|
||||
# 保存到文件
|
||||
with open(RESULTS_FILE, 'a', encoding='utf-8') as f:
|
||||
f.write("="*80 + "\n")
|
||||
f.write(f"发现时间: {timestamp}\n")
|
||||
f.write(f"助记词: {mnemonic}\n")
|
||||
f.write(f"地址: {address}\n")
|
||||
f.write(f"私钥: {private_key}\n")
|
||||
f.write("余额信息:\n")
|
||||
for balance in balances:
|
||||
f.write(f" {balance['chain'].upper()}: {balance['balance_formatted']}\n")
|
||||
f.write("="*80 + "\n\n")
|
||||
|
||||
# 同时保存JSON格式
|
||||
json_file = RESULTS_FILE.replace('.txt', '.json')
|
||||
try:
|
||||
if os.path.exists(json_file):
|
||||
with open(json_file, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
else:
|
||||
data = []
|
||||
|
||||
data.append(info)
|
||||
with open(json_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def worker_thread(wallet_tool, thread_id):
|
||||
"""工作线程:生成助记词并检查余额"""
|
||||
global stats
|
||||
|
||||
# 每个线程独立计数,减少锁竞争
|
||||
local_count = 0
|
||||
thread_start_time = time.time()
|
||||
|
||||
while True:
|
||||
try:
|
||||
# 生成助记词
|
||||
mnemonic = wallet_tool.generate_mnemonic()
|
||||
|
||||
# 生成地址
|
||||
address = wallet_tool.mnemonic_to_address(mnemonic)
|
||||
if not address:
|
||||
local_count += 1
|
||||
# 立即更新统计(不等待)
|
||||
if local_count >= 1: # 每次都更新,确保统计正常
|
||||
with stats['lock']:
|
||||
stats['total_checked'] += local_count
|
||||
local_count = 0
|
||||
continue
|
||||
|
||||
# 先更新统计(在查询余额之前)
|
||||
local_count += 1
|
||||
if local_count >= 1: # 每次都更新
|
||||
with stats['lock']:
|
||||
stats['total_checked'] += local_count
|
||||
total = stats['total_checked']
|
||||
found = stats['found_balance']
|
||||
if total % 20 == 0: # 每20次打印一次
|
||||
elapsed = time.time() - stats['start_time']
|
||||
speed = total / elapsed if elapsed > 0 else 0
|
||||
logger.info(f"已检查: {total} | 发现: {found} | 速度: {speed:.1f} 个/秒")
|
||||
local_count = 0
|
||||
|
||||
# 快速检查余额(只检查主要链,设置超时)
|
||||
try:
|
||||
balance_info = wallet_tool.quick_check_balance(address)
|
||||
except:
|
||||
balance_info = None
|
||||
|
||||
# 如果发现余额
|
||||
if balance_info:
|
||||
# 生成私钥
|
||||
account = Account.from_mnemonic(mnemonic, account_path="m/44'/60'/0'/0/0")
|
||||
private_key = account.key.hex()
|
||||
|
||||
# 检查所有链的余额
|
||||
all_balances = wallet_tool.check_all_chains(address)
|
||||
|
||||
# 保存结果
|
||||
save_found_wallet(mnemonic, address, private_key, all_balances)
|
||||
|
||||
# 更新统计并打印
|
||||
with stats['lock']:
|
||||
stats['found_balance'] += 1
|
||||
|
||||
logger.success(f"🎉 发现余额钱包!")
|
||||
logger.success(f"地址: {address}")
|
||||
logger.success(f"助记词: {mnemonic}")
|
||||
for bal in all_balances:
|
||||
logger.success(f" {bal['chain'].upper()}: {bal['balance_formatted']}")
|
||||
|
||||
except Exception as e:
|
||||
# 即使出错也更新统计
|
||||
local_count += 1
|
||||
if local_count >= 1:
|
||||
with stats['lock']:
|
||||
stats['total_checked'] += local_count
|
||||
local_count = 0
|
||||
continue
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
print("="*80)
|
||||
print("EVM助记词碰撞工具 - 高速版本")
|
||||
print("="*80)
|
||||
print(f"结果将保存到: {RESULTS_FILE}")
|
||||
print(f"查询的链: ethereum, bsc, polygon, arbitrum, base")
|
||||
print("="*80)
|
||||
print()
|
||||
|
||||
# 选择要查询的链(可以修改这里)
|
||||
chains = ["ethereum", "bsc", "polygon", "arbitrum", "base"]
|
||||
|
||||
# 创建钱包工具
|
||||
wallet_tool = FastEVMWallet(chains=chains)
|
||||
|
||||
# 设置线程数(根据CPU核心数调整)
|
||||
num_threads = os.cpu_count() or 4
|
||||
print(f"启动 {num_threads} 个工作线程...")
|
||||
print("按 Ctrl+C 停止程序\n")
|
||||
|
||||
# 创建线程
|
||||
threads = []
|
||||
|
||||
for i in range(num_threads):
|
||||
t = threading.Thread(target=worker_thread, args=(wallet_tool, i), daemon=True)
|
||||
t.start()
|
||||
threads.append(t)
|
||||
|
||||
# 等待一小段时间确保线程启动
|
||||
time.sleep(1)
|
||||
|
||||
# 检查是否有线程在运行
|
||||
active_threads = sum(1 for t in threads if t.is_alive())
|
||||
if active_threads == 0:
|
||||
logger.error("所有工作线程都停止了,请检查RPC连接")
|
||||
return
|
||||
else:
|
||||
logger.info(f"✓ {active_threads} 个工作线程正在运行")
|
||||
|
||||
# 测试:生成一个测试地址并查询,验证功能是否正常
|
||||
logger.info("正在测试功能...")
|
||||
test_mnemonic = wallet_tool.generate_mnemonic()
|
||||
test_address = wallet_tool.mnemonic_to_address(test_mnemonic)
|
||||
if test_address:
|
||||
logger.info(f"测试地址生成成功: {test_address[:10]}...")
|
||||
test_balance = wallet_tool.quick_check_balance(test_address)
|
||||
if test_balance is None:
|
||||
logger.info("测试余额查询成功(无余额,正常)")
|
||||
else:
|
||||
logger.warning(f"测试发现余额!这不太可能,请检查")
|
||||
else:
|
||||
logger.error("测试地址生成失败!")
|
||||
|
||||
try:
|
||||
# 主线程等待
|
||||
last_count = 0
|
||||
while True:
|
||||
time.sleep(2) # 每2秒更新一次显示
|
||||
# 定期打印统计信息
|
||||
elapsed = time.time() - stats['start_time']
|
||||
current_count = stats['total_checked']
|
||||
|
||||
# 如果计数没有变化,可能是线程有问题
|
||||
if elapsed > 10 and current_count == last_count and current_count == 0:
|
||||
logger.warning("警告:工作线程可能没有正常工作,请检查RPC连接")
|
||||
|
||||
if elapsed > 0:
|
||||
speed = current_count / elapsed if current_count > 0 else 0
|
||||
print(f"\r运行时间: {int(elapsed)}秒 | 已检查: {current_count} | 发现: {stats['found_balance']} | 速度: {speed:.1f} 个/秒", end='', flush=True)
|
||||
|
||||
last_count = current_count
|
||||
except KeyboardInterrupt:
|
||||
print("\n\n正在停止...")
|
||||
print(f"\n最终统计:")
|
||||
print(f" 总检查数: {stats['total_checked']}")
|
||||
print(f" 发现余额: {stats['found_balance']}")
|
||||
print(f" 结果已保存到: {RESULTS_FILE}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
BIN
kline_data.xlsx
BIN
kline_data.xlsx
Binary file not shown.
483
mexc/30分钟.py
483
mexc/30分钟.py
@@ -1,483 +0,0 @@
|
||||
"""
|
||||
量化交易回测系统 - 30分钟K线策略回测(Weex数据源)
|
||||
|
||||
========== 策略规则 ==========
|
||||
重要:所有开仓和平仓操作都在下一根K线的开盘价执行
|
||||
|
||||
【策略流程】
|
||||
1. 开仓条件(信号出现时,下一根K线开盘价开仓):
|
||||
- 阳包阴(涨包跌)信号 -> 开多
|
||||
* 前一根是跌(阴线),后一根是涨(阳线)
|
||||
* 且:涨的收盘价 > 跌的开盘价
|
||||
|
||||
- 阴包阳(跌包涨)信号 -> 开空
|
||||
* 前一根是涨(阳线),后一根是跌(阴线)
|
||||
* 且:跌的收盘价 < 涨的开盘价
|
||||
|
||||
2. 平仓条件(所有平仓都在下一根K线开盘价执行):
|
||||
- 持有多单时:遇到两根连续的阴线 -> 下一根K线开盘价平仓
|
||||
- 持有空单时:遇到两根连续的阳线 -> 下一根K线开盘价平仓
|
||||
- 遇到反向信号:下一根K线开盘价平仓并反手开仓
|
||||
* 例如:持有多单时遇到阴包阳信号 -> 平多开空
|
||||
|
||||
3. 续持条件:
|
||||
- 遇到同向信号:续持
|
||||
- 未满足平仓条件:续持
|
||||
|
||||
【数据处理流程】
|
||||
1. 从数据库读取数据
|
||||
2. 数据排序(按时间戳升序)
|
||||
3. 开始回测
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import calendar
|
||||
from dataclasses import dataclass
|
||||
from typing import List, Dict, Optional
|
||||
from loguru import logger
|
||||
|
||||
from models.mexc import Mexc30
|
||||
|
||||
|
||||
# ========================= 工具函数 =========================
|
||||
|
||||
# 交易对的最小价格单位(tick size)配置
|
||||
# 格式:交易对符号 -> 最小单位
|
||||
TICK_SIZE_MAP = {
|
||||
'SOLUSDT': 0.01,
|
||||
'BTCUSDT': 0.1,
|
||||
'ETHUSDT': 0.01,
|
||||
'BNBUSDT': 0.01,
|
||||
# 可以根据需要添加更多交易对
|
||||
}
|
||||
|
||||
# 默认最小单位(如果交易对不在配置中)
|
||||
DEFAULT_TICK_SIZE = 0.01
|
||||
|
||||
|
||||
def get_tick_size(symbol: str) -> float:
|
||||
"""
|
||||
获取交易对的最小价格单位
|
||||
:param symbol: 交易对符号,如 'SOLUSDT'
|
||||
:return: 最小单位,如 0.01
|
||||
"""
|
||||
return TICK_SIZE_MAP.get(symbol.upper(), DEFAULT_TICK_SIZE)
|
||||
|
||||
|
||||
def adjust_price_for_trade(price: float, direction: str, symbol: str = 'SOLUSDT') -> float:
|
||||
"""
|
||||
根据交易方向调整价格,考虑买卖价差
|
||||
|
||||
买入(开多/平空):价格 + tick_size
|
||||
卖出(开空/平多):价格 - tick_size
|
||||
|
||||
:param price: 原始价格(K线开盘价或收盘价)
|
||||
:param direction: 交易方向,'long' 表示买入,'short' 表示卖出
|
||||
:param symbol: 交易对符号,用于获取最小单位
|
||||
:return: 调整后的价格
|
||||
"""
|
||||
tick_size = get_tick_size(symbol)
|
||||
|
||||
if direction == 'long':
|
||||
# 买入:价格 + tick_size
|
||||
adjusted = price + tick_size
|
||||
elif direction == 'short':
|
||||
# 卖出:价格 - tick_size
|
||||
adjusted = price - tick_size
|
||||
else:
|
||||
# 未知方向,返回原价
|
||||
adjusted = price
|
||||
|
||||
# 确保价格不会为负
|
||||
return max(adjusted, tick_size)
|
||||
|
||||
|
||||
def is_bullish(c): # 阳线
|
||||
return float(c['close']) > float(c['open'])
|
||||
|
||||
|
||||
def is_bearish(c): # 阴线
|
||||
return float(c['close']) < float(c['open'])
|
||||
|
||||
|
||||
def check_signal(prev, curr):
|
||||
"""
|
||||
包住形态信号判定(优化版):
|
||||
只看两种信号,严格按照收盘价与开盘价的比较:
|
||||
|
||||
1. 阳包阴(涨包跌,前跌后涨)-> 做多:
|
||||
- 前一根是跌(阴线:close < open)
|
||||
- 后一根是涨(阳线:close > open)
|
||||
- 且:涨的收盘价 > 跌的开盘价(curr['close'] > prev['open'])
|
||||
|
||||
2. 阴包阳(跌包涨,前涨后跌)-> 做空:
|
||||
- 前一根是涨(阳线:close > open)
|
||||
- 后一根是跌(阴线:close < open)
|
||||
- 且:跌的收盘价 < 涨的开盘价(curr['close'] < prev['open'])
|
||||
"""
|
||||
p_open = float(prev['open'])
|
||||
c_close = float(curr['close'])
|
||||
|
||||
# 阳包阴(涨包跌,前跌后涨) -> 做多:涨的收盘价 > 跌的开盘价
|
||||
if is_bearish(prev) and is_bullish(curr) and c_close > p_open:
|
||||
return "long", "bear_bull_engulf"
|
||||
|
||||
# 阴包阳(跌包涨,前涨后跌) -> 做空:跌的收盘价 < 涨的开盘价
|
||||
if is_bullish(prev) and is_bearish(curr) and c_close < p_open:
|
||||
return "short", "bull_bear_engulf"
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
def get_data_by_date(model, date_str: str):
|
||||
"""
|
||||
按天获取指定表的数据(30分钟K线)
|
||||
数据格式:时间戳(毫秒级) 开盘价 最高价 最低价 收盘价
|
||||
例如:1767461400000 3106.68 3109.1 3106.22 3107.22
|
||||
|
||||
注意:返回的数据已按时间戳(id)升序排序
|
||||
"""
|
||||
try:
|
||||
target_date = datetime.datetime.strptime(date_str, '%Y-%m-%d')
|
||||
except ValueError:
|
||||
logger.error("日期格式不正确,请使用 YYYY-MM-DD 格式。")
|
||||
return []
|
||||
|
||||
# 将日期转换为毫秒级时间戳进行查询
|
||||
start_ts = int(target_date.timestamp() * 1000)
|
||||
end_ts = int((target_date + datetime.timedelta(days=1)).timestamp() * 1000) - 1
|
||||
|
||||
# 查询时按时间戳升序排序
|
||||
query = model.select().where(model.id.between(start_ts, end_ts)).order_by(model.id.asc())
|
||||
data = [{'id': i.id, 'open': i.open, 'high': i.high, 'low': i.low, 'close': i.close} for i in query]
|
||||
|
||||
# 确保数据已排序
|
||||
if data:
|
||||
data.sort(key=lambda x: x['id'])
|
||||
|
||||
return data
|
||||
|
||||
|
||||
# ========================= 回测逻辑 =========================
|
||||
|
||||
def backtest_15m_trend_optimized(dates: List[str], symbol: str = 'SOLUSDT'):
|
||||
"""
|
||||
回测策略逻辑:
|
||||
1. 开仓条件(信号出现时,下一根K线开盘价开仓):
|
||||
- 阳包阴(涨包跌)信号 -> 开多
|
||||
- 阴包阳(跌包涨)信号 -> 开空
|
||||
|
||||
2. 平仓条件(所有平仓都在下一根K线开盘价执行):
|
||||
- 持有多单时:遇到两根连续的阴线 -> 下一根K线开盘价平仓
|
||||
- 持有空单时:遇到两根连续的阳线 -> 下一根K线开盘价平仓
|
||||
- 遇到反向信号:下一根K线开盘价平仓并反手开仓
|
||||
(例如:持有多单时遇到阴包阳信号 -> 平多开空)
|
||||
|
||||
3. 续持条件:
|
||||
- 遇到同向信号:续持
|
||||
- 未满足平仓条件:续持
|
||||
"""
|
||||
# ==================== 步骤1:从数据库读取数据 ====================
|
||||
all_data: List[Dict] = []
|
||||
total_queried = 0
|
||||
for d in dates:
|
||||
day_data = get_data_by_date(Mexc30, d)
|
||||
all_data.extend(day_data)
|
||||
if day_data:
|
||||
total_queried += len(day_data)
|
||||
|
||||
logger.info(f"总共查询了 {len(dates)} 天,获取到 {total_queried} 条K线数据")
|
||||
|
||||
if not all_data:
|
||||
logger.warning("未获取到任何数据,请检查数据库连接和日期范围")
|
||||
return [], {
|
||||
'bear_bull_engulf': {'count': 0, 'wins': 0, 'total_profit': 0.0, 'name': '涨包跌'},
|
||||
'bull_bear_engulf': {'count': 0, 'wins': 0, 'total_profit': 0.0, 'name': '跌包涨'},
|
||||
}
|
||||
|
||||
# ==================== 步骤2:数据去重 + 排序(重要!必须先处理再回测) ====================
|
||||
# 以时间戳(id)为唯一键去重,保留最先出现的一条
|
||||
unique_by_id: Dict[int, Dict] = {}
|
||||
for item in all_data:
|
||||
if item['id'] not in unique_by_id:
|
||||
unique_by_id[item['id']] = item
|
||||
all_data = list(unique_by_id.values())
|
||||
all_data.sort(key=lambda x: x['id'])
|
||||
|
||||
# 验证排序结果
|
||||
if len(all_data) > 1:
|
||||
first_ts = all_data[0]['id']
|
||||
last_ts = all_data[-1]['id']
|
||||
first_time = datetime.datetime.fromtimestamp(first_ts / 1000)
|
||||
last_time = datetime.datetime.fromtimestamp(last_ts / 1000)
|
||||
logger.info(f"数据已按时间排序:{first_time.strftime('%Y-%m-%d %H:%M:%S')} 到 {last_time.strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
|
||||
# ==================== 步骤3:开始回测 ====================
|
||||
|
||||
stats = {
|
||||
'bear_bull_engulf': {'count': 0, 'wins': 0, 'total_profit': 0.0, 'name': '涨包跌'},
|
||||
'bull_bear_engulf': {'count': 0, 'wins': 0, 'total_profit': 0.0, 'name': '跌包涨'},
|
||||
}
|
||||
|
||||
trades: List[Dict] = []
|
||||
current_position: Optional[Dict] = None # 开仓信息
|
||||
consecutive_opposite_count = 0 # 连续反色K线计数
|
||||
idx = 1
|
||||
|
||||
while idx < len(all_data) - 1:
|
||||
prev, curr, next_bar = all_data[idx - 1], all_data[idx], all_data[idx + 1]
|
||||
direction, signal_key = check_signal(prev, curr)
|
||||
|
||||
# ==================== 空仓状态:遇到信号则开仓 ====================
|
||||
# 策略:阳包阴信号 -> 开多,阴包阳信号 -> 开空
|
||||
if current_position is None:
|
||||
if direction:
|
||||
# 信号出现(prev和curr形成信号),在下一根K线(next_bar)的开盘价开仓
|
||||
# 应用买卖价差:买入(开多)时价格+0.01,卖出(开空)时价格-0.01
|
||||
raw_price = float(next_bar['open'])
|
||||
entry_price = adjust_price_for_trade(raw_price, direction, symbol)
|
||||
current_position = {
|
||||
'direction': direction,
|
||||
'signal': stats[signal_key]['name'],
|
||||
'signal_key': signal_key,
|
||||
'entry_price': entry_price,
|
||||
'entry_time': next_bar['id']
|
||||
}
|
||||
consecutive_opposite_count = 0 # 重置连续反色计数
|
||||
stats[signal_key]['count'] += 1
|
||||
logger.debug(f"开仓: {stats[signal_key]['name']} {'做多' if direction == 'long' else '做空'} @ {entry_price:.2f} (原始价格: {raw_price:.2f})")
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
# ==================== 有仓位状态:检查平仓条件 ====================
|
||||
pos_dir = current_position['direction']
|
||||
pos_sig_key = current_position['signal_key']
|
||||
|
||||
# 1. 反向信号 -> 下一根K线开盘价平仓并反手开仓
|
||||
# 策略:遇到反向信号(如持有多单时遇到阴包阳),平仓并反手开仓
|
||||
if direction and direction != pos_dir:
|
||||
# 平仓:持有多单时卖出(价格-0.01),持有空单时买入(价格+0.01)
|
||||
raw_exit_price = float(next_bar['open'])
|
||||
exit_direction = 'short' if pos_dir == 'long' else 'long' # 平仓方向与持仓方向相反
|
||||
exit_price = adjust_price_for_trade(raw_exit_price, exit_direction, symbol)
|
||||
|
||||
diff = (exit_price - current_position['entry_price']) if pos_dir == 'long' else (
|
||||
current_position['entry_price'] - exit_price)
|
||||
trades.append({
|
||||
'entry_time': datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
'exit_time': datetime.datetime.fromtimestamp(next_bar['id'] / 1000),
|
||||
'signal': current_position['signal'],
|
||||
'direction': '做多' if pos_dir == 'long' else '做空',
|
||||
'entry': current_position['entry_price'],
|
||||
'exit': exit_price,
|
||||
'diff': diff
|
||||
})
|
||||
stats[pos_sig_key]['total_profit'] += diff
|
||||
if diff > 0: stats[pos_sig_key]['wins'] += 1
|
||||
|
||||
# 反手开仓(下一根K线开盘价,应用买卖价差)
|
||||
raw_entry_price = float(next_bar['open'])
|
||||
entry_price = adjust_price_for_trade(raw_entry_price, direction, symbol)
|
||||
current_position = {
|
||||
'direction': direction,
|
||||
'signal': stats[signal_key]['name'],
|
||||
'signal_key': signal_key,
|
||||
'entry_price': entry_price,
|
||||
'entry_time': next_bar['id']
|
||||
}
|
||||
consecutive_opposite_count = 0 # 重置连续反色计数
|
||||
stats[signal_key]['count'] += 1
|
||||
logger.debug(f"反向信号反手: 平{'做多' if pos_dir == 'long' else '做空'} @ {exit_price:.2f} (原始: {raw_exit_price:.2f}), 开{'做多' if direction == 'long' else '做空'} @ {entry_price:.2f} (原始: {raw_entry_price:.2f})")
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
# 2. 检查连续反色K线平仓条件(下一根K线开盘价平仓)
|
||||
# 策略:持有多单时,遇到两根连续的阴线 -> 平仓
|
||||
# 持有空单时,遇到两根连续的阳线 -> 平仓
|
||||
if pos_dir == 'long' and is_bearish(curr):
|
||||
consecutive_opposite_count += 1
|
||||
# 如果已经连续两根阴线,下一根K线开盘价平仓
|
||||
if consecutive_opposite_count >= 2:
|
||||
logger.debug(f"平仓: 做多遇到连续两根阴线")
|
||||
# 平多单:卖出,价格 - 0.01
|
||||
raw_exit_price = float(next_bar['open'])
|
||||
exit_price = adjust_price_for_trade(raw_exit_price, 'short', symbol)
|
||||
diff = exit_price - current_position['entry_price']
|
||||
trades.append({
|
||||
'entry_time': datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
'exit_time': datetime.datetime.fromtimestamp(next_bar['id'] / 1000),
|
||||
'signal': current_position['signal'],
|
||||
'direction': '做多',
|
||||
'entry': current_position['entry_price'],
|
||||
'exit': exit_price,
|
||||
'diff': diff
|
||||
})
|
||||
stats[pos_sig_key]['total_profit'] += diff
|
||||
if diff > 0: stats[pos_sig_key]['wins'] += 1
|
||||
current_position = None
|
||||
consecutive_opposite_count = 0
|
||||
idx += 1
|
||||
continue
|
||||
else:
|
||||
# 只有一根阴线,续持
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
# 持有空单:检查是否连续两根阳线
|
||||
elif pos_dir == 'short' and is_bullish(curr):
|
||||
consecutive_opposite_count += 1
|
||||
# 如果已经连续两根阳线,下一根K线开盘价平仓
|
||||
if consecutive_opposite_count >= 2:
|
||||
logger.debug(f"平仓: 做空遇到连续两根阳线")
|
||||
# 平空单:买入,价格 + 0.01
|
||||
raw_exit_price = float(next_bar['open'])
|
||||
exit_price = adjust_price_for_trade(raw_exit_price, 'long', symbol)
|
||||
diff = current_position['entry_price'] - exit_price
|
||||
trades.append({
|
||||
'entry_time': datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
'exit_time': datetime.datetime.fromtimestamp(next_bar['id'] / 1000),
|
||||
'signal': current_position['signal'],
|
||||
'direction': '做空',
|
||||
'entry': current_position['entry_price'],
|
||||
'exit': exit_price,
|
||||
'diff': diff
|
||||
})
|
||||
stats[pos_sig_key]['total_profit'] += diff
|
||||
if diff > 0: stats[pos_sig_key]['wins'] += 1
|
||||
current_position = None
|
||||
consecutive_opposite_count = 0
|
||||
idx += 1
|
||||
continue
|
||||
else:
|
||||
# 只有一根阳线,续持
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
# 3. 同向K线或同向信号 -> 续持,重置连续反色计数
|
||||
if (pos_dir == 'long' and is_bullish(curr)) or (pos_dir == 'short' and is_bearish(curr)):
|
||||
consecutive_opposite_count = 0 # 重置连续反色计数
|
||||
|
||||
# 同向信号 -> 续持
|
||||
if direction and direction == pos_dir:
|
||||
consecutive_opposite_count = 0 # 重置连续反色计数
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
idx += 1
|
||||
|
||||
# 尾仓:最后一根收盘价平仓
|
||||
if current_position:
|
||||
last = all_data[-1]
|
||||
pos_dir = current_position['direction']
|
||||
# 平仓:持有多单时卖出(价格-0.01),持有空单时买入(价格+0.01)
|
||||
raw_exit_price = float(last['close'])
|
||||
exit_direction = 'short' if pos_dir == 'long' else 'long' # 平仓方向与持仓方向相反
|
||||
exit_price = adjust_price_for_trade(raw_exit_price, exit_direction, symbol)
|
||||
|
||||
diff = (exit_price - current_position['entry_price']) if pos_dir == 'long' else (
|
||||
current_position['entry_price'] - exit_price)
|
||||
trades.append({
|
||||
'entry_time': datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
'exit_time': datetime.datetime.fromtimestamp(last['id'] / 1000),
|
||||
'signal': current_position['signal'],
|
||||
'direction': '做多' if pos_dir == 'long' else '做空',
|
||||
'entry': current_position['entry_price'],
|
||||
'exit': exit_price,
|
||||
'diff': diff
|
||||
})
|
||||
stats[current_position['signal_key']]['total_profit'] += diff
|
||||
if diff > 0: stats[current_position['signal_key']]['wins'] += 1
|
||||
|
||||
return trades, stats
|
||||
|
||||
|
||||
# ========================= 运行示例(优化版盈利计算) =========================
|
||||
if __name__ == '__main__':
|
||||
dates = []
|
||||
# 获取当前日期
|
||||
today = datetime.datetime.now()
|
||||
target_year = 2025
|
||||
|
||||
for month in range(1, 13):
|
||||
# 获取该月的实际天数
|
||||
days_in_month = calendar.monthrange(target_year, month)[1]
|
||||
for day in range(1, days_in_month + 1):
|
||||
# 只添加今天及之前的日期
|
||||
date_str = f"{target_year}-{month:02d}-{day:02d}"
|
||||
date_obj = datetime.datetime.strptime(date_str, '%Y-%m-%d')
|
||||
# 如果日期在今天之后,跳过
|
||||
if date_obj > today:
|
||||
break
|
||||
dates.append(date_str)
|
||||
|
||||
print(dates)
|
||||
|
||||
# dates = [f"2025-09-{i}" for i in range(1, 32)]
|
||||
# 指定交易对符号,用于获取正确的最小价格单位
|
||||
symbol = 'SOLUSDT'
|
||||
trades, stats = backtest_15m_trend_optimized(dates, symbol=symbol)
|
||||
|
||||
logger.info("===== 每笔交易详情 =====")
|
||||
|
||||
# === 参数设定 ===
|
||||
contract_size = 10000 # 合约规模(1手对应多少基础货币)
|
||||
open_fee_fixed = 5 # 固定开仓手续费
|
||||
close_fee_rate = 0.0005 # 按成交额比例的平仓手续费率
|
||||
|
||||
total_points_profit = 0 # 累计点差
|
||||
total_money_profit = 0 # 累计金额盈利
|
||||
total_fee = 0 # 累计手续费
|
||||
|
||||
for t in trades:
|
||||
entry = t['entry']
|
||||
exit = t['exit']
|
||||
direction = t['direction']
|
||||
|
||||
# === 1️⃣ 原始价差(点差) ===
|
||||
point_diff = (exit - entry) if direction == '做多' else (entry - exit)
|
||||
|
||||
# === 2️⃣ 金额盈利(考虑合约规模) ===
|
||||
money_profit = point_diff / entry * contract_size # 利润以基础货币计(例如USD)
|
||||
|
||||
# === 3️⃣ 手续费计算 ===
|
||||
# 开仓 + 平仓手续费(按比例计算 + 固定)
|
||||
fee = open_fee_fixed + (contract_size / entry * exit * close_fee_rate)
|
||||
|
||||
# === 4️⃣ 净利润 ===
|
||||
net_profit = money_profit - fee
|
||||
|
||||
# 保存计算结果
|
||||
t.update({
|
||||
'point_diff': point_diff,
|
||||
'raw_profit': money_profit,
|
||||
'fee': fee,
|
||||
'net_profit': net_profit
|
||||
})
|
||||
|
||||
total_points_profit += point_diff
|
||||
total_money_profit += money_profit
|
||||
total_fee += fee
|
||||
|
||||
# if net_profit < -400:
|
||||
logger.info(
|
||||
f"{t['entry_time']} {direction}({t['signal']}) "
|
||||
f"入={entry:.2f} 出={exit:.2f} 差价={point_diff:.2f} "
|
||||
f"原始盈利={money_profit:.2f} 手续费={fee:.2f} 净利润={net_profit:.2f} {t['exit_time']}"
|
||||
)
|
||||
|
||||
# === 汇总统计 ===
|
||||
total_net_profit = total_money_profit - total_fee
|
||||
print(f"\n一共交易笔数:{len(trades)}")
|
||||
print(f"总点差:{total_points_profit:.2f}")
|
||||
print(f"总原始盈利(未扣费):{total_money_profit:.2f}")
|
||||
print(f"总手续费:{total_fee:.2f}")
|
||||
print(f"总净利润:{total_net_profit:.2f}\n")
|
||||
|
||||
print(total_money_profit - total_fee * 0.1)
|
||||
|
||||
print("===== 信号统计 =====")
|
||||
for k, v in stats.items():
|
||||
name, count, wins, total_p = v['name'], v['count'], v['wins'], v['total_profit']
|
||||
win_rate = (wins / count * 100) if count > 0 else 0.0
|
||||
avg_p = (total_p / count) if count > 0 else 0.0
|
||||
print(f"{name}: 次数={count} 胜率={win_rate:.2f}% 总价差={total_p:.2f} 平均价差={avg_p:.2f}")
|
||||
659
mexc/获取数据.py
659
mexc/获取数据.py
@@ -1,659 +0,0 @@
|
||||
import requests
|
||||
import pandas as pd
|
||||
import datetime
|
||||
import time
|
||||
from models.mexc import Mexc1, Mexc15, Mexc30, Mexc1Hour
|
||||
|
||||
|
||||
def get_mexc_klines(symbol="SOLUSDT", interval="30m", limit=500):
|
||||
url = "https://api.mexc.com/api/v3/klines"
|
||||
headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
||||
}
|
||||
params = {
|
||||
"symbol": symbol.upper(),
|
||||
"interval": interval,
|
||||
"limit": limit
|
||||
}
|
||||
|
||||
try:
|
||||
resp = requests.get(url, params=params, headers=headers, timeout=10)
|
||||
resp.raise_for_status() # 提前抛出 HTTP 错误
|
||||
data = resp.json()
|
||||
|
||||
if not isinstance(data, list) or not data:
|
||||
print("返回数据异常:", data)
|
||||
return None
|
||||
|
||||
# 使用正确的 8 列
|
||||
columns = [
|
||||
"open_time", "open", "high", "low", "close",
|
||||
"volume", "close_time", "quote_volume"
|
||||
]
|
||||
|
||||
df = pd.DataFrame(data, columns=columns)
|
||||
|
||||
# 保存原始时间戳(用于数据库id)
|
||||
df["open_time_ms"] = df["open_time"].astype(int)
|
||||
|
||||
# 时间戳转可读时间
|
||||
df["open_time"] = pd.to_datetime(df["open_time"], unit="ms")
|
||||
df["close_time"] = pd.to_datetime(df["close_time"], unit="ms")
|
||||
|
||||
# 常用字段排序 + 类型转换
|
||||
df = df[[
|
||||
"open_time", "open", "high", "low", "close",
|
||||
"volume", "quote_volume", "open_time_ms"
|
||||
]]
|
||||
|
||||
# 转成浮点数(方便后续计算)
|
||||
for col in ["open", "high", "low", "close", "volume", "quote_volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
|
||||
print(f"成功获取 {len(df)} 条 {interval} K线({symbol})")
|
||||
return df
|
||||
|
||||
except Exception as e:
|
||||
print("请求失败:", str(e))
|
||||
return None
|
||||
|
||||
|
||||
def normalize_futures_symbol(symbol):
|
||||
"""将现货格式转换为合约格式,如 SOLUSDT -> SOL_USDT"""
|
||||
if "_" in symbol:
|
||||
return symbol.upper()
|
||||
symbol = symbol.upper()
|
||||
for quote in ["USDT", "USDC", "USD", "BTC", "ETH"]:
|
||||
if symbol.endswith(quote):
|
||||
base = symbol[:-len(quote)]
|
||||
return f"{base}_{quote}"
|
||||
return symbol
|
||||
|
||||
|
||||
def map_futures_interval(interval):
|
||||
"""合约K线周期映射"""
|
||||
mapping = {
|
||||
"1m": "Min1",
|
||||
"5m": "Min5",
|
||||
"15m": "Min15",
|
||||
"30m": "Min30",
|
||||
"1h": "Min60",
|
||||
"4h": "Hour4",
|
||||
"8h": "Hour8",
|
||||
"1d": "Day1",
|
||||
"1w": "Week1",
|
||||
"1M": "Month1",
|
||||
}
|
||||
return mapping.get(interval, interval)
|
||||
|
||||
|
||||
def get_mexc_klines_with_time(symbol="SOLUSDT", interval="30m", start_time_ms=None, end_time_ms=None, limit=1000):
|
||||
"""
|
||||
获取指定时间范围内的K线数据
|
||||
:param symbol: 交易对符号
|
||||
:param interval: 时间间隔,如 "30m", "1m", "15m", "1h"
|
||||
:param start_time_ms: 开始时间(毫秒级时间戳)
|
||||
:param end_time_ms: 结束时间(毫秒级时间戳)
|
||||
:param limit: 单次获取数量(最大1000)
|
||||
:return: DataFrame或None
|
||||
"""
|
||||
def _fetch(params):
|
||||
headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
||||
}
|
||||
resp = requests.get(url, params=params, headers=headers, timeout=10)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
return resp, data
|
||||
|
||||
url = "https://api.mexc.com/api/v3/klines"
|
||||
params = {
|
||||
"symbol": symbol.upper(),
|
||||
"interval": interval,
|
||||
"limit": limit
|
||||
}
|
||||
if start_time_ms:
|
||||
params["startTime"] = start_time_ms
|
||||
if end_time_ms:
|
||||
params["endTime"] = end_time_ms
|
||||
|
||||
try:
|
||||
resp, data = _fetch(params)
|
||||
|
||||
# 检查是否是错误响应
|
||||
if isinstance(data, dict) and 'code' in data:
|
||||
print(f"API返回错误: {data}, 请求参数: {params}")
|
||||
return None
|
||||
|
||||
# 尝试秒级时间戳回退(部分接口可能只接受秒)
|
||||
if (not isinstance(data, list) or not data) and (start_time_ms or end_time_ms):
|
||||
params_s = params.copy()
|
||||
if start_time_ms:
|
||||
params_s["startTime"] = int(start_time_ms // 1000)
|
||||
if end_time_ms:
|
||||
params_s["endTime"] = int(end_time_ms // 1000)
|
||||
resp_s, data_s = _fetch(params_s)
|
||||
if isinstance(data_s, list) and data_s:
|
||||
print("检测到秒级时间戳可用,已自动回退到秒级参数")
|
||||
resp, data = resp_s, data_s
|
||||
params = params_s
|
||||
|
||||
if not isinstance(data, list) or not data:
|
||||
print(f"API返回数据异常: {data}, 请求参数: {params}")
|
||||
print(f"完整URL: {resp.url}")
|
||||
return None
|
||||
|
||||
columns = [
|
||||
"open_time", "open", "high", "low", "close",
|
||||
"volume", "close_time", "quote_volume"
|
||||
]
|
||||
|
||||
df = pd.DataFrame(data, columns=columns)
|
||||
# 判断时间单位(秒/毫秒)
|
||||
max_open = pd.to_numeric(df["open_time"], errors="coerce").max()
|
||||
if max_open < 1_000_000_000_000: # 小于1e12视为秒
|
||||
df["open_time_ms"] = (df["open_time"].astype(int) * 1000)
|
||||
df["open_time"] = pd.to_datetime(df["open_time"], unit="s")
|
||||
df["close_time"] = pd.to_datetime(df["close_time"], unit="s")
|
||||
else:
|
||||
df["open_time_ms"] = df["open_time"].astype(int)
|
||||
df["open_time"] = pd.to_datetime(df["open_time"], unit="ms")
|
||||
df["close_time"] = pd.to_datetime(df["close_time"], unit="ms")
|
||||
|
||||
df = df[[
|
||||
"open_time", "open", "high", "low", "close",
|
||||
"volume", "quote_volume", "open_time_ms"
|
||||
]]
|
||||
|
||||
for col in ["open", "high", "low", "close", "volume", "quote_volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
|
||||
return df
|
||||
|
||||
except Exception as e:
|
||||
print(f"请求失败: {str(e)}")
|
||||
return None
|
||||
|
||||
|
||||
def get_mexc_futures_klines(symbol="SOLUSDT", interval="30m", limit=500):
|
||||
return get_mexc_futures_klines_with_time(
|
||||
symbol=symbol,
|
||||
interval=interval,
|
||||
start_time_ms=None,
|
||||
end_time_ms=None,
|
||||
limit=limit
|
||||
)
|
||||
|
||||
|
||||
def get_mexc_futures_klines_with_time(symbol="SOLUSDT", interval="30m", start_time_ms=None, end_time_ms=None, limit=1000):
|
||||
"""
|
||||
获取合约K线数据(使用 contract.mexc.com)
|
||||
"""
|
||||
url = "https://contract.mexc.com/api/v1/contract/kline"
|
||||
symbol = normalize_futures_symbol(symbol)
|
||||
interval = map_futures_interval(interval)
|
||||
|
||||
def _fetch(params):
|
||||
headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
||||
"Accept": "application/json, text/plain, */*",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
resp = requests.get(url, params=params, headers=headers, timeout=10)
|
||||
resp.raise_for_status()
|
||||
return resp, resp.json()
|
||||
|
||||
params = {
|
||||
"symbol": symbol,
|
||||
"interval": interval,
|
||||
"limit": limit
|
||||
}
|
||||
# 合约接口通常使用 start/end(秒级),这里默认秒级
|
||||
if start_time_ms:
|
||||
params["start"] = int(start_time_ms // 1000)
|
||||
if end_time_ms:
|
||||
params["end"] = int(end_time_ms // 1000)
|
||||
|
||||
try:
|
||||
resp, data = _fetch(params)
|
||||
|
||||
# 兼容错误结构
|
||||
if isinstance(data, dict) and ("success" in data and data.get("success") is False):
|
||||
print(f"合约API返回错误: {data}, 请求参数: {params}")
|
||||
return None
|
||||
|
||||
payload = data
|
||||
if isinstance(data, dict) and "data" in data:
|
||||
payload = data["data"]
|
||||
|
||||
# 兼容返回为 dict of arrays
|
||||
if isinstance(payload, dict) and "time" in payload:
|
||||
times = payload.get("time", [])
|
||||
opens = payload.get("open", [])
|
||||
highs = payload.get("high", [])
|
||||
lows = payload.get("low", [])
|
||||
closes = payload.get("close", [])
|
||||
vols = payload.get("vol", [])
|
||||
rows = list(zip(times, opens, highs, lows, closes, vols))
|
||||
payload = rows
|
||||
|
||||
if not isinstance(payload, list) or not payload:
|
||||
print(f"合约API返回数据异常: {payload}, 请求参数: {params}")
|
||||
print(f"完整URL: {resp.url}")
|
||||
return None
|
||||
|
||||
# 兼容 list of lists: [time, open, high, low, close, vol, ...]
|
||||
columns = ["open_time", "open", "high", "low", "close", "volume"]
|
||||
df = pd.DataFrame(payload, columns=columns + list(range(len(payload[0]) - len(columns))))
|
||||
df = df[columns]
|
||||
|
||||
# 判断时间单位(秒/毫秒)
|
||||
max_open = pd.to_numeric(df["open_time"], errors="coerce").max()
|
||||
if max_open < 1_000_000_000_000:
|
||||
df["open_time_ms"] = (df["open_time"].astype(int) * 1000)
|
||||
df["open_time"] = pd.to_datetime(df["open_time"], unit="s")
|
||||
else:
|
||||
df["open_time_ms"] = df["open_time"].astype(int)
|
||||
df["open_time"] = pd.to_datetime(df["open_time"], unit="ms")
|
||||
|
||||
for col in ["open", "high", "low", "close", "volume"]:
|
||||
df[col] = pd.to_numeric(df[col], errors="coerce")
|
||||
|
||||
# 合约接口不一定返回 quote_volume,这里保持一致字段结构
|
||||
df["quote_volume"] = pd.NA
|
||||
df = df[[
|
||||
"open_time", "open", "high", "low", "close",
|
||||
"volume", "quote_volume", "open_time_ms"
|
||||
]]
|
||||
|
||||
return df
|
||||
|
||||
except Exception as e:
|
||||
print(f"合约请求失败: {str(e)}")
|
||||
return None
|
||||
|
||||
|
||||
def get_interval_ms(interval):
|
||||
"""
|
||||
将时间间隔字符串转换为毫秒数
|
||||
:param interval: 如 "1m", "15m", "30m", "1h"
|
||||
:return: 毫秒数
|
||||
"""
|
||||
if interval.endswith("m"):
|
||||
minutes = int(interval[:-1])
|
||||
return minutes * 60 * 1000
|
||||
elif interval.endswith("h"):
|
||||
hours = int(interval[:-1])
|
||||
return hours * 60 * 60 * 1000
|
||||
elif interval.endswith("d"):
|
||||
days = int(interval[:-1])
|
||||
return days * 24 * 60 * 60 * 1000
|
||||
else:
|
||||
return 60 * 1000 # 默认1分钟
|
||||
|
||||
|
||||
def fetch_historical_data(symbol="SOLUSDT", interval="30m", start_date="2024-01-01", end_date=None, auto_save=True, mode="auto", market="spot"):
|
||||
"""
|
||||
批量获取历史K线数据并存储到数据库
|
||||
:param symbol: 交易对符号
|
||||
:param interval: 时间间隔,如 "30m", "1m", "15m", "1h"
|
||||
:param start_date: 开始日期,格式 "YYYY-MM-DD",默认 "2024-01-01"
|
||||
:param end_date: 结束日期,格式 "YYYY-MM-DD",默认当前时间
|
||||
:param auto_save: 是否自动保存到数据库,默认True
|
||||
:param mode: 获取模式,"auto" 自动判断,"forward" 正向,"reverse" 反向
|
||||
:return: 总共获取的数据条数
|
||||
"""
|
||||
# 转换日期为时间戳(毫秒)
|
||||
start_dt = datetime.datetime.strptime(start_date, "%Y-%m-%d")
|
||||
start_dt = start_dt.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
start_time_ms = int(start_dt.timestamp() * 1000)
|
||||
|
||||
if end_date:
|
||||
end_dt = datetime.datetime.strptime(end_date, "%Y-%m-%d")
|
||||
end_dt = end_dt.replace(hour=23, minute=59, second=59, microsecond=999999)
|
||||
else:
|
||||
end_dt = datetime.datetime.now()
|
||||
end_time_ms = int(end_dt.timestamp() * 1000)
|
||||
|
||||
print(f"开始获取 {symbol} {interval} K线数据")
|
||||
print(f"时间范围: {start_date} 至 {end_dt.strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
|
||||
current_start = start_time_ms
|
||||
total_count = 0
|
||||
limit = 1000 # API最大限制
|
||||
interval_ms = get_interval_ms(interval)
|
||||
|
||||
# 计算每次请求的时间窗口(1000条数据的时间跨度)
|
||||
window_ms = limit * interval_ms
|
||||
|
||||
if mode == "reverse":
|
||||
return fetch_historical_data_reverse(
|
||||
symbol=symbol,
|
||||
interval=interval,
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
auto_save=auto_save,
|
||||
market=market
|
||||
)
|
||||
|
||||
fetch_fn = get_mexc_klines_with_time
|
||||
latest_fn = get_mexc_klines
|
||||
if market == "futures":
|
||||
fetch_fn = get_mexc_futures_klines_with_time
|
||||
latest_fn = get_mexc_futures_klines
|
||||
|
||||
if mode == "auto":
|
||||
# 探测:如果 start+end 为空、start 单独返回最新数据,说明 startTime 被忽略,改用反向获取
|
||||
probe_end = min(start_time_ms + window_ms, end_time_ms)
|
||||
probe_df = fetch_fn(
|
||||
symbol=symbol,
|
||||
interval=interval,
|
||||
start_time_ms=start_time_ms,
|
||||
end_time_ms=probe_end,
|
||||
limit=10
|
||||
)
|
||||
if probe_df is None or probe_df.empty:
|
||||
probe_df2 = fetch_fn(
|
||||
symbol=symbol,
|
||||
interval=interval,
|
||||
start_time_ms=start_time_ms,
|
||||
end_time_ms=None,
|
||||
limit=10
|
||||
)
|
||||
if probe_df2 is not None and not probe_df2.empty:
|
||||
latest_ms = int(probe_df2.iloc[-1]["open_time_ms"])
|
||||
if latest_ms > probe_end:
|
||||
print("检测到 startTime 被忽略,自动改为反向获取模式")
|
||||
return fetch_historical_data_reverse(
|
||||
symbol=symbol,
|
||||
interval=interval,
|
||||
start_date=start_date,
|
||||
end_date=end_date,
|
||||
auto_save=auto_save,
|
||||
market=market
|
||||
)
|
||||
|
||||
while current_start < end_time_ms:
|
||||
current_end = min(current_start + window_ms, end_time_ms)
|
||||
|
||||
print(f"正在获取: {datetime.datetime.fromtimestamp(current_start/1000)} 至 {datetime.datetime.fromtimestamp(current_end/1000)}")
|
||||
print(f"时间戳: {current_start} 至 {current_end}")
|
||||
|
||||
df = fetch_fn(
|
||||
symbol=symbol,
|
||||
interval=interval,
|
||||
start_time_ms=current_start,
|
||||
end_time_ms=current_end,
|
||||
limit=limit
|
||||
)
|
||||
|
||||
if df is None or df.empty:
|
||||
# 先测试一下不带时间参数的最新数据是否能获取到
|
||||
if current_start == start_time_ms and total_count == 0:
|
||||
print("\n=== 开始调试 ===")
|
||||
print("测试1:尝试获取最新数据(不带时间参数)...")
|
||||
test_df = latest_fn(symbol=symbol, interval=interval, limit=10)
|
||||
if test_df is not None and not test_df.empty:
|
||||
print(f"✓ 测试1成功!最新数据可以获取")
|
||||
print(f" 最新时间: {test_df.iloc[-1]['open_time']}")
|
||||
print(f" 最新时间戳: {test_df.iloc[-1]['open_time_ms']}")
|
||||
|
||||
# 测试2:只带startTime,不带endTime
|
||||
print(f"\n测试2:尝试只带startTime(从{start_date}开始)...")
|
||||
test_df2 = fetch_fn(
|
||||
symbol=symbol,
|
||||
interval=interval,
|
||||
start_time_ms=current_start,
|
||||
end_time_ms=None,
|
||||
limit=100
|
||||
)
|
||||
if test_df2 is not None and not test_df2.empty:
|
||||
print(f"✓ 测试2成功!从指定时间开始获取数据")
|
||||
print(f" 获取到 {len(test_df2)} 条数据")
|
||||
print(f" 第一条时间: {test_df2.iloc[0]['open_time']}")
|
||||
print(f" 最后一条时间: {test_df2.iloc[-1]['open_time']}")
|
||||
print("=== 调试结束 ===\n")
|
||||
# 使用测试2的方法继续,赋值给df
|
||||
df = test_df2
|
||||
# 不break,继续下面的处理流程
|
||||
else:
|
||||
print("✗ 测试2失败:带startTime无法获取数据")
|
||||
print("\n建议:MEXC API可能不支持从历史日期开始查询")
|
||||
print("可以尝试:1. 检查时间范围是否在API支持范围内")
|
||||
print(" 2. 使用反向获取:从最新数据往前推")
|
||||
print("=== 调试结束 ===\n")
|
||||
break
|
||||
else:
|
||||
print("✗ 测试1失败:即使不带时间参数也无法获取数据")
|
||||
print("可能原因:交易对符号或时间间隔不正确")
|
||||
print("=== 调试结束 ===\n")
|
||||
break
|
||||
|
||||
# 如果测试后df仍然是None或空,说明无法获取数据
|
||||
if df is None or df.empty:
|
||||
if total_count > 0:
|
||||
print("已到达数据末尾")
|
||||
else:
|
||||
print("本次获取数据为空,跳过")
|
||||
break
|
||||
|
||||
count = len(df)
|
||||
total_count += count
|
||||
print(f"本次获取 {count} 条数据,累计 {total_count} 条")
|
||||
|
||||
# 自动保存到数据库
|
||||
if auto_save:
|
||||
save_to_database(df, interval=interval, symbol=symbol)
|
||||
|
||||
# 更新下一次的起始时间(从最后一条数据的开盘时间 + 一个间隔)
|
||||
last_open_ms = int(df.iloc[-1]['open_time_ms'])
|
||||
current_start = last_open_ms + interval_ms
|
||||
|
||||
# 避免请求过快
|
||||
time.sleep(0.2)
|
||||
|
||||
print(f"完成!总共获取 {total_count} 条 {symbol} {interval} K线数据")
|
||||
return total_count
|
||||
|
||||
|
||||
def fetch_historical_data_reverse(symbol="SOLUSDT", interval="30m", start_date="2024-01-01", end_date=None, auto_save=True, market="spot"):
|
||||
"""
|
||||
反向获取历史K线数据(从最新往前推,使用startTime参数)
|
||||
"""
|
||||
start_dt = datetime.datetime.strptime(start_date, "%Y-%m-%d")
|
||||
start_dt = start_dt.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
start_time_ms = int(start_dt.timestamp() * 1000)
|
||||
|
||||
if end_date:
|
||||
end_dt = datetime.datetime.strptime(end_date, "%Y-%m-%d")
|
||||
end_dt = end_dt.replace(hour=23, minute=59, second=59, microsecond=999999)
|
||||
else:
|
||||
end_dt = datetime.datetime.now()
|
||||
end_time_ms = int(end_dt.timestamp() * 1000)
|
||||
|
||||
print(f"开始反向获取 {symbol} {interval} K线数据(从最新往前推)")
|
||||
print(f"目标时间范围: {start_date} 至 {end_dt.strftime('%Y-%m-%d %H:%M:%S')}")
|
||||
|
||||
total_count = 0
|
||||
limit = 1000 # 每次尝试获取1000条
|
||||
interval_ms = get_interval_ms(interval)
|
||||
|
||||
# 使用带时间参数的函数
|
||||
fetch_fn = get_mexc_klines_with_time
|
||||
if market == "futures":
|
||||
fetch_fn = get_mexc_futures_klines_with_time
|
||||
|
||||
# 先获取最新数据,确定当前最新时间
|
||||
print("\n第 1 批:获取最新数据...")
|
||||
df_latest = get_mexc_klines(symbol=symbol, interval=interval, limit=100)
|
||||
if df_latest is None or df_latest.empty:
|
||||
print("无法获取最新数据,停止")
|
||||
return 0
|
||||
|
||||
latest_time_ms = int(df_latest.iloc[-1]["open_time_ms"])
|
||||
earliest_fetched_ms = int(df_latest.iloc[0]["open_time_ms"])
|
||||
|
||||
# 保存第一批数据
|
||||
df_latest_filtered = df_latest[df_latest["open_time_ms"] >= start_time_ms]
|
||||
if not df_latest_filtered.empty:
|
||||
count = len(df_latest_filtered)
|
||||
total_count += count
|
||||
print(f"获取 {count} 条最新数据,时间范围: {datetime.datetime.fromtimestamp(earliest_fetched_ms/1000)} 至 {datetime.datetime.fromtimestamp(latest_time_ms/1000)}")
|
||||
if auto_save:
|
||||
save_to_database(df_latest_filtered, interval=interval, symbol=symbol)
|
||||
|
||||
# 从已获取的最早时间往前推
|
||||
current_start_ms = earliest_fetched_ms - interval_ms
|
||||
batch_num = 1
|
||||
|
||||
while current_start_ms >= start_time_ms:
|
||||
batch_num += 1
|
||||
# 计算本次请求的时间窗口(往前推limit条数据的时间跨度)
|
||||
window_ms = limit * interval_ms
|
||||
batch_start_ms = max(current_start_ms - window_ms, start_time_ms)
|
||||
|
||||
print(f"\n第 {batch_num} 批:从 {datetime.datetime.fromtimestamp(batch_start_ms/1000)} 开始获取...")
|
||||
|
||||
# 使用startTime参数,尝试获取更早的数据
|
||||
df = fetch_fn(
|
||||
symbol=symbol,
|
||||
interval=interval,
|
||||
start_time_ms=batch_start_ms,
|
||||
end_time_ms=current_start_ms,
|
||||
limit=limit
|
||||
)
|
||||
|
||||
if df is None or df.empty:
|
||||
# 如果返回空,尝试只使用startTime,不带endTime
|
||||
print(" 带endTime返回空,尝试只使用startTime...")
|
||||
df = fetch_fn(
|
||||
symbol=symbol,
|
||||
interval=interval,
|
||||
start_time_ms=batch_start_ms,
|
||||
end_time_ms=None,
|
||||
limit=limit
|
||||
)
|
||||
|
||||
if df is None or df.empty:
|
||||
print(f" 无法获取数据,可能已到达API历史数据限制")
|
||||
break
|
||||
|
||||
# 检查返回的数据是否真的从startTime开始(判断startTime是否被忽略)
|
||||
returned_earliest = int(df.iloc[0]["open_time_ms"])
|
||||
returned_latest = int(df.iloc[-1]["open_time_ms"])
|
||||
|
||||
# 如果返回的数据仍然是最新的,说明startTime被忽略
|
||||
if returned_latest >= latest_time_ms - 1000: # 允许1秒误差
|
||||
print(f" 检测到startTime被忽略(返回最新数据),无法继续往前获取")
|
||||
break
|
||||
|
||||
# 过滤掉已获取的数据和早于start_date的数据
|
||||
df = df[df["open_time_ms"] < current_start_ms]
|
||||
df = df[df["open_time_ms"] >= start_time_ms]
|
||||
|
||||
if df.empty:
|
||||
print(f" 过滤后无新数据")
|
||||
# 如果已经到达目标起始时间,完成
|
||||
if batch_start_ms <= start_time_ms:
|
||||
print(f"已到达目标起始时间({start_date}),完成")
|
||||
break
|
||||
# 否则继续往前推
|
||||
current_start_ms = batch_start_ms - interval_ms
|
||||
time.sleep(0.3)
|
||||
continue
|
||||
|
||||
# 更新已获取的最早时间戳
|
||||
current_earliest = int(df.iloc[0]["open_time_ms"])
|
||||
if current_earliest < earliest_fetched_ms:
|
||||
earliest_fetched_ms = current_earliest
|
||||
|
||||
count = len(df)
|
||||
total_count += count
|
||||
print(f" 获取 {count} 条新数据,最早时间: {datetime.datetime.fromtimestamp(current_earliest/1000)}")
|
||||
print(f" 累计获取 {total_count} 条数据")
|
||||
|
||||
if auto_save:
|
||||
save_to_database(df, interval=interval, symbol=symbol)
|
||||
|
||||
# 更新下一次的起始时间
|
||||
current_start_ms = current_earliest - interval_ms
|
||||
|
||||
# 避免请求过快
|
||||
time.sleep(0.3)
|
||||
|
||||
# 安全限制
|
||||
if batch_num > 500:
|
||||
print("批次过多,停止")
|
||||
break
|
||||
|
||||
print(f"\n完成!总共获取 {total_count} 条 {symbol} {interval} K线数据")
|
||||
if earliest_fetched_ms:
|
||||
print(f"最早数据时间: {datetime.datetime.fromtimestamp(earliest_fetched_ms/1000)}")
|
||||
if earliest_fetched_ms > start_time_ms:
|
||||
print(f"⚠️ 注意:最早数据时间 ({datetime.datetime.fromtimestamp(earliest_fetched_ms/1000)}) 晚于目标起始时间 ({start_date})")
|
||||
print(f" 这可能是因为MEXC API的历史数据限制,无法获取更早的数据")
|
||||
return total_count
|
||||
|
||||
|
||||
def save_to_database(df, interval="30m", symbol="SOLUSDT"):
|
||||
"""
|
||||
将K线数据存储到数据库
|
||||
:param df: pandas DataFrame,包含K线数据
|
||||
:param interval: 时间间隔,如 "30m", "1m", "15m", "1h"
|
||||
:param symbol: 交易对符号
|
||||
"""
|
||||
if df is None or df.empty:
|
||||
print("数据为空,无法存储")
|
||||
return
|
||||
|
||||
# 根据时间间隔选择对应的模型
|
||||
interval_map = {
|
||||
"1m": Mexc1,
|
||||
"15m": Mexc15,
|
||||
"30m": Mexc30,
|
||||
"1h": Mexc1Hour,
|
||||
}
|
||||
|
||||
model_class = interval_map.get(interval)
|
||||
if model_class is None:
|
||||
print(f"不支持的时间间隔: {interval}")
|
||||
return
|
||||
|
||||
# 存储数据到数据库
|
||||
saved_count = 0
|
||||
for _, row in df.iterrows():
|
||||
# 使用原始时间戳(毫秒级)作为 id
|
||||
timestamp_ms = int(row['open_time_ms'])
|
||||
|
||||
model_class.get_or_create(
|
||||
id=timestamp_ms,
|
||||
defaults={
|
||||
'open': float(row['open']),
|
||||
'high': float(row['high']),
|
||||
'low': float(row['low']),
|
||||
'close': float(row['close']),
|
||||
}
|
||||
)
|
||||
saved_count += 1
|
||||
|
||||
print(f"成功存储 {saved_count} 条 {interval} K线数据({symbol})到数据库")
|
||||
|
||||
|
||||
# 使用示例
|
||||
if __name__ == "__main__":
|
||||
# 方式1: 获取最新200条数据
|
||||
# df = get_mexc_klines("SOLUSDT", "30m", 200)
|
||||
# if df is not None:
|
||||
# print(df.tail(8))
|
||||
# save_to_database(df, interval="30m", symbol="SOLUSDT")
|
||||
|
||||
# 方式2: 批量获取2024年至今的历史数据(使用现货接口,反向获取)
|
||||
fetch_historical_data(
|
||||
symbol="SOLUSDT",
|
||||
interval="30m",
|
||||
start_date="2024-01-01",
|
||||
end_date=None, # None表示到当前时间
|
||||
auto_save=True,
|
||||
market="spot", # 使用现货接口
|
||||
mode="reverse" # 强制使用反向获取模式(从最新往前推)
|
||||
)
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
[]
|
||||
211
okx/test.py
211
okx/test.py
@@ -1,211 +0,0 @@
|
||||
import datetime
|
||||
import json
|
||||
import time
|
||||
import requests
|
||||
from loguru import logger
|
||||
|
||||
# ================== 通用请求头 ==================
|
||||
headers = {
|
||||
'accept': 'application/json, text/plain, */*',
|
||||
'accept-language': 'zh,zh-CN;q=0.9,zh-HK;q=0.8,en;q=0.7',
|
||||
'cache-control': 'no-cache',
|
||||
'origin': 'https://www.okx.com',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=1, i',
|
||||
'referer': 'https://www.okx.com/',
|
||||
'sec-ch-ua': '"Chromium";v="140", "Not=A?Brand";v="24", "Google Chrome";v="140"',
|
||||
'sec-ch-ua-mobile': '?0',
|
||||
'sec-ch-ua-platform': '"Windows"',
|
||||
'sec-fetch-dest': 'empty',
|
||||
'sec-fetch-mode': 'cors',
|
||||
'sec-fetch-site': 'same-site',
|
||||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36',
|
||||
}
|
||||
|
||||
|
||||
# ================== 获取 OKX K线数据 ==================
|
||||
def fetch_kline_okx(day: int, year: int = 2025, month: int = 9, period='15m', symbol="ETH-USDT"):
|
||||
"""
|
||||
从 OKX 获取指定日期的K线数据
|
||||
:param day: 日期
|
||||
:param year: 年份
|
||||
:param month: 月份
|
||||
:param period: K线周期 (如 '1m', '5m', '15m', '1H')
|
||||
:param symbol: 交易对
|
||||
"""
|
||||
time_ser = datetime.datetime(year, month, day)
|
||||
start_of_day = int(time_ser.replace(hour=0, minute=0, second=0, microsecond=0).timestamp() * 1000)
|
||||
end_of_day = int(time_ser.replace(hour=23, minute=59, second=59, microsecond=0).timestamp() * 1000)
|
||||
|
||||
url = "https://www.okx.com/api/v5/market/candles"
|
||||
params = {
|
||||
"instId": symbol,
|
||||
"bar": period,
|
||||
"limit": "1000"
|
||||
}
|
||||
|
||||
all_data = []
|
||||
next_start = end_of_day
|
||||
|
||||
while True:
|
||||
params["after"] = next_start
|
||||
resp = requests.get(url, params=params, headers=headers)
|
||||
data = resp.json().get("data", [])
|
||||
|
||||
if not data:
|
||||
break
|
||||
|
||||
for d in data:
|
||||
candle = {
|
||||
"id": int(int(d[0]) / 1000), # 秒级时间戳
|
||||
"open": d[1],
|
||||
"high": d[2],
|
||||
"low": d[3],
|
||||
"close": d[4],
|
||||
"volume": d[5]
|
||||
}
|
||||
all_data.append(candle)
|
||||
|
||||
next_start = int(data[-1][0])
|
||||
if len(data) < 1000:
|
||||
break
|
||||
|
||||
# OKX返回数据按时间倒序排列
|
||||
return sorted(all_data, key=lambda x: x['id'])
|
||||
|
||||
|
||||
# ================== 辅助函数 ==================
|
||||
def is_bullish(candle):
|
||||
"""判断是否是阳线(涨)"""
|
||||
return float(candle['open']) < float(candle['close'])
|
||||
|
||||
|
||||
def is_bearish(candle):
|
||||
"""判断是否是阴线(跌)"""
|
||||
return float(candle['open']) > float(candle['close'])
|
||||
|
||||
|
||||
def check_signal(prev, curr):
|
||||
"""
|
||||
判断包住形态信号:
|
||||
1. 前跌后涨包住 -> 做多信号 (bear_bull_engulf)
|
||||
2. 前涨后跌包住 -> 做空信号 (bull_bear_engulf)
|
||||
"""
|
||||
p_open, p_close = float(prev['open']), float(prev['close'])
|
||||
c_open, c_close = float(curr['open']), float(curr['close'])
|
||||
|
||||
# 前跌后涨包住 -> 做多
|
||||
if is_bullish(curr) and is_bearish(prev):
|
||||
if c_open < p_close and c_close > p_open:
|
||||
return "long", "bear_bull_engulf"
|
||||
|
||||
# 前涨后跌包住 -> 做空
|
||||
if is_bearish(curr) and is_bullish(prev):
|
||||
if c_open > p_close and c_close < p_open:
|
||||
return "short", "bull_bear_engulf"
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
def simulate_trade(direction, entry_price, future_candles, take_profit_diff=30, stop_loss_diff=-10):
|
||||
"""
|
||||
模拟逐根K线的止盈止损
|
||||
"""
|
||||
for candle in future_candles:
|
||||
high, low, close = float(candle['high']), float(candle['low']), float(candle['close'])
|
||||
|
||||
if direction == "long":
|
||||
if high >= entry_price + take_profit_diff:
|
||||
return entry_price + take_profit_diff, take_profit_diff, candle['id'] # 止盈
|
||||
if low <= entry_price + stop_loss_diff:
|
||||
return entry_price + stop_loss_diff, stop_loss_diff, candle['id'] # 止损
|
||||
|
||||
elif direction == "short":
|
||||
if low <= entry_price - take_profit_diff:
|
||||
return entry_price - take_profit_diff, take_profit_diff, candle['id']
|
||||
if high >= entry_price - stop_loss_diff:
|
||||
return entry_price - stop_loss_diff, stop_loss_diff, candle['id']
|
||||
|
||||
final_price = float(future_candles[-1]['close'])
|
||||
diff_money = (final_price - entry_price) if direction == "long" else (entry_price - final_price)
|
||||
return final_price, diff_money, future_candles[-1]['id']
|
||||
|
||||
|
||||
# ================== 主程序 ==================
|
||||
if __name__ == '__main__':
|
||||
all_trades = []
|
||||
signal_stats = {
|
||||
"bear_bull_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "涨包跌"},
|
||||
"bull_bear_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "跌包涨"}
|
||||
}
|
||||
|
||||
datas = []
|
||||
for i in range(1, 31):
|
||||
logger.info(f"Fetching 2025-09-{i}")
|
||||
sorted_data = fetch_kline_okx(year=2025, month=1, day=i, period='15m', symbol="ETH-USDT")
|
||||
datas.extend(sorted_data)
|
||||
time.sleep(0.3)
|
||||
|
||||
# # 可选:保存数据方便离线分析
|
||||
# with open("okx_eth_15m_sep2025.json", "w", encoding="utf-8") as f:
|
||||
# json.dump(datas, f, ensure_ascii=False, indent=2)
|
||||
|
||||
datas = sorted(datas, key=lambda x: x["id"])
|
||||
|
||||
daily_signals = daily_wins = daily_profit = 0
|
||||
|
||||
for idx in range(1, len(datas) - 2):
|
||||
prev, curr = datas[idx - 1], datas[idx]
|
||||
entry_candle = datas[idx + 1]
|
||||
future_candles = datas[idx + 2:]
|
||||
|
||||
entry_open = float(entry_candle['open'])
|
||||
direction, signal_type = check_signal(prev, curr)
|
||||
|
||||
if direction and signal_type:
|
||||
daily_signals += 1
|
||||
exit_price, diff, exit_time = simulate_trade(direction, entry_open, future_candles,
|
||||
take_profit_diff=30, stop_loss_diff=-2)
|
||||
|
||||
signal_stats[signal_type]["count"] += 1
|
||||
signal_stats[signal_type]["total_profit"] += diff
|
||||
if diff > 0:
|
||||
signal_stats[signal_type]["wins"] += 1
|
||||
daily_wins += 1
|
||||
|
||||
daily_profit += diff
|
||||
|
||||
local_time = datetime.datetime.fromtimestamp(entry_candle['id'])
|
||||
formatted_time = local_time.strftime("%Y-%m-%d %H:%M")
|
||||
|
||||
all_trades.append((
|
||||
formatted_time,
|
||||
"做多" if direction == "long" else "做空",
|
||||
signal_stats[signal_type]["name"],
|
||||
entry_open,
|
||||
exit_price,
|
||||
diff,
|
||||
exit_time
|
||||
))
|
||||
|
||||
# ================= 输出交易详情 =================
|
||||
logger.info("===== 每笔交易详情 =====")
|
||||
n = n1 = 0
|
||||
for date, direction, signal_name, entry, exit, diff, end_time in all_trades:
|
||||
profit_amount = diff / entry * 10000
|
||||
close_fee = 10000 / entry * exit * 0.0005
|
||||
|
||||
logger.info(
|
||||
f"{date} {direction}({signal_name}) 入场={entry:.2f} 出场={exit:.2f} 出场时间={end_time} "
|
||||
f"差价={diff:.2f} 盈利={profit_amount:.2f} "
|
||||
f"开仓手续费=5u 平仓手续费={close_fee:.2f}"
|
||||
)
|
||||
n1 += 5 + close_fee
|
||||
n += profit_amount
|
||||
|
||||
print("=" * 60)
|
||||
print(f"交易总数:{len(all_trades)}")
|
||||
print(f"总盈利:{n:.2f} u")
|
||||
print(f"总手续费:{n1:.2f} u")
|
||||
print(f"净利润:{n - n1:.2f} u")
|
||||
print("=" * 60)
|
||||
@@ -1,25 +0,0 @@
|
||||
# 基于开盘价的五分之一策略
|
||||
|
||||
策略规则(1111):
|
||||
|
||||
- **做多触发价** = 当前K线开盘价 + 前一根实体/5
|
||||
- **做空触发价** = 当前K线开盘价 - 前一根实体/5
|
||||
- **前一根有效K线**:实体 ≥ 0.1
|
||||
|
||||
## 执行逻辑
|
||||
|
||||
- 当前K线最高价 ≥ 做多触发价 → 做多信号
|
||||
- 当前K线最低价 ≤ 做空触发价 → 做空信号
|
||||
- 同根K线多空都触及时,用1分钟K线判断先后
|
||||
- 触及信号则开仓或反手,同根3分钟K线只交易一次
|
||||
|
||||
## 运行
|
||||
|
||||
```bash
|
||||
cd /path/to/lm_code
|
||||
python open_fifth_strategy/main.py
|
||||
```
|
||||
|
||||
## 配置
|
||||
|
||||
在 `config.py` 中修改 API、合约、杠杆等参数。
|
||||
@@ -1,2 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""基于开盘价的五分之一策略"""
|
||||
@@ -1,26 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
基于开盘价的五分之一策略 - 配置文件
|
||||
|
||||
策略规则(1111):
|
||||
- 做多触发价 = 当前K线开盘价 + 前一根实体/5
|
||||
- 做空触发价 = 当前K线开盘价 - 前一根实体/5
|
||||
- 前一根有效K线:实体 >= 0.1
|
||||
"""
|
||||
|
||||
# BitMart API(请勿提交敏感信息到版本库)
|
||||
API_KEY = "a0fb7b98464fd9bcce67e7c519d58ec10d0c38a8"
|
||||
SECRET_KEY = "4eaeba78e77aeaab1c2027f846a276d164f264a44c2c1bb1c5f3be50c8de1ca5"
|
||||
MEMO = "合约交易"
|
||||
|
||||
# 交易参数
|
||||
CONTRACT_SYMBOL = "ETHUSDT"
|
||||
KLINE_STEP = 3 # 3分钟K线
|
||||
MIN_BODY_SIZE = 0.1 # 有效K线最小实体
|
||||
CHECK_INTERVAL = 3 # 检测间隔(秒)
|
||||
LEVERAGE = "100"
|
||||
OPEN_TYPE = "cross" # 全仓
|
||||
RISK_PERCENT = 0.01 # 每次开仓占用可用余额的比例
|
||||
|
||||
# 比特浏览器ID(用于网页下单)
|
||||
BIT_ID = "f2320f57e24c45529a009e1541e25961"
|
||||
@@ -1,536 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
BitMart 基于开盘价的五分之一策略交易
|
||||
|
||||
策略规则(1111):
|
||||
- 做多触发价 = 当前K线开盘价 + 实体/5
|
||||
- 做空触发价 = 当前K线开盘价 - 实体/5
|
||||
- 基于前一根有效K线(实体 >= 0.1)
|
||||
|
||||
执行:触及做多/做空触发价则开仓或反手,同根K线只交易一次
|
||||
|
||||
运行:python open_fifth_strategy/main.py(在项目根目录 lm_code 下)
|
||||
"""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
_root = Path(__file__).resolve().parent.parent
|
||||
if str(_root) not in sys.path:
|
||||
sys.path.insert(0, str(_root))
|
||||
|
||||
import random
|
||||
import time
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
from loguru import logger
|
||||
from bit_tools import openBrowser
|
||||
from DrissionPage import ChromiumPage
|
||||
from DrissionPage import ChromiumOptions
|
||||
|
||||
from bitmart.api_contract import APIContract
|
||||
from 交易.tools import send_dingtalk_message
|
||||
|
||||
from open_fifth_strategy.config import (
|
||||
API_KEY,
|
||||
SECRET_KEY,
|
||||
MEMO,
|
||||
CONTRACT_SYMBOL,
|
||||
KLINE_STEP,
|
||||
MIN_BODY_SIZE,
|
||||
CHECK_INTERVAL,
|
||||
LEVERAGE,
|
||||
OPEN_TYPE,
|
||||
RISK_PERCENT,
|
||||
BIT_ID,
|
||||
)
|
||||
|
||||
ding_executor = ThreadPoolExecutor(max_workers=2, thread_name_prefix="dingtalk")
|
||||
|
||||
|
||||
class OpenBasedFifthStrategy:
|
||||
"""基于开盘价的五分之一策略"""
|
||||
|
||||
def __init__(self, bit_id=None):
|
||||
self.page = None
|
||||
self.api_key = API_KEY
|
||||
self.secret_key = SECRET_KEY
|
||||
self.memo = MEMO
|
||||
self.contract_symbol = CONTRACT_SYMBOL
|
||||
self.contractAPI = APIContract(
|
||||
self.api_key, self.secret_key, self.memo, timeout=(5, 15)
|
||||
)
|
||||
|
||||
self.start = 0 # 持仓: -1空, 0无, 1多
|
||||
self.open_avg_price = None
|
||||
self.current_amount = None
|
||||
self.position_cross = None
|
||||
self.bit_id = bit_id or BIT_ID
|
||||
|
||||
self.min_body_size = MIN_BODY_SIZE
|
||||
self.kline_step = KLINE_STEP
|
||||
self.check_interval = CHECK_INTERVAL
|
||||
self.leverage = LEVERAGE
|
||||
self.open_type = OPEN_TYPE
|
||||
self.risk_percent = RISK_PERCENT
|
||||
|
||||
self.last_trigger_kline_id = None
|
||||
self.last_trigger_direction = None
|
||||
self.last_trade_kline_id = None
|
||||
|
||||
# ==================== 策略核心 ====================
|
||||
|
||||
def get_body_size(self, candle):
|
||||
return abs(float(candle["open"]) - float(candle["close"]))
|
||||
|
||||
def find_valid_prev_bar(self, all_data, current_idx):
|
||||
"""找前一根有效K线(实体>=min_body_size)"""
|
||||
if current_idx <= 0:
|
||||
return None, None
|
||||
for i in range(current_idx - 1, -1, -1):
|
||||
prev = all_data[i]
|
||||
if self.get_body_size(prev) >= self.min_body_size:
|
||||
return i, prev
|
||||
return None, None
|
||||
|
||||
def get_trigger_levels(self, prev, curr_open):
|
||||
"""
|
||||
基于当前K线开盘价计算触发价(1111)
|
||||
做多触发 = 当前K线开盘价 + 实体/5
|
||||
做空触发 = 当前K线开盘价 - 实体/5
|
||||
"""
|
||||
body = self.get_body_size(prev)
|
||||
if body < 0.001:
|
||||
return None, None
|
||||
curr_o = float(curr_open)
|
||||
return curr_o + body / 5, curr_o - body / 5
|
||||
|
||||
def get_1m_bars_for_3m_bar(self, bar_3m):
|
||||
"""获取当前3分钟K线对应的3根1分钟K线"""
|
||||
try:
|
||||
start_ts = int(bar_3m["id"])
|
||||
end_ts = start_ts + 3 * 60
|
||||
response = self.contractAPI.get_kline(
|
||||
contract_symbol=self.contract_symbol,
|
||||
step=1,
|
||||
start_time=start_ts,
|
||||
end_time=end_ts,
|
||||
)[0]
|
||||
if response.get("code") != 1000:
|
||||
return []
|
||||
data = response.get("data", [])
|
||||
out = []
|
||||
for k in data:
|
||||
out.append(
|
||||
{
|
||||
"id": int(k["timestamp"]),
|
||||
"open": float(k["open_price"]),
|
||||
"high": float(k["high_price"]),
|
||||
"low": float(k["low_price"]),
|
||||
"close": float(k["close_price"]),
|
||||
}
|
||||
)
|
||||
out.sort(key=lambda x: x["id"])
|
||||
return out
|
||||
except Exception as e:
|
||||
logger.warning(f"获取1分钟K线失败: {e}")
|
||||
return []
|
||||
|
||||
def determine_trigger_order_by_1m(self, bars_1m, long_trigger, short_trigger):
|
||||
"""同根K线多空都触及时,用1分钟K线判断先后"""
|
||||
if not bars_1m:
|
||||
return None
|
||||
for bar in bars_1m:
|
||||
high = float(bar["high"])
|
||||
low = float(bar["low"])
|
||||
open_price = float(bar["open"])
|
||||
long_ok = high >= long_trigger
|
||||
short_ok = low <= short_trigger
|
||||
if long_ok and not short_ok:
|
||||
return "long"
|
||||
if short_ok and not long_ok:
|
||||
return "short"
|
||||
if long_ok and short_ok:
|
||||
d_long = abs(long_trigger - open_price)
|
||||
d_short = abs(short_trigger - open_price)
|
||||
return "short" if d_short < d_long else "long"
|
||||
return None
|
||||
|
||||
def check_trigger(self, kline_data):
|
||||
"""
|
||||
检测当前K线是否触发信号(1111:当前开盘价±实体/5)
|
||||
返回:(方向, 触发价, 有效前一根, 当前K线) 或 (None,...)
|
||||
"""
|
||||
if len(kline_data) < 2:
|
||||
return None, None, None, None
|
||||
|
||||
curr = kline_data[-1]
|
||||
curr_kline_id = curr["id"]
|
||||
curr_open = curr["open"]
|
||||
_, prev = self.find_valid_prev_bar(kline_data, len(kline_data) - 1)
|
||||
if prev is None:
|
||||
return None, None, None, None
|
||||
|
||||
long_trigger, short_trigger = self.get_trigger_levels(prev, curr_open)
|
||||
if long_trigger is None:
|
||||
return None, None, None, None
|
||||
|
||||
c_high = float(curr["high"])
|
||||
c_low = float(curr["low"])
|
||||
long_triggered = c_high >= long_trigger
|
||||
short_triggered = c_low <= short_trigger
|
||||
|
||||
direction = None
|
||||
trigger_price = None
|
||||
|
||||
if long_triggered and short_triggered:
|
||||
bars_1m = self.get_1m_bars_for_3m_bar(curr)
|
||||
if bars_1m:
|
||||
direction = self.determine_trigger_order_by_1m(
|
||||
bars_1m, long_trigger, short_trigger
|
||||
)
|
||||
trigger_price = long_trigger if direction == "long" else short_trigger
|
||||
if direction is None:
|
||||
c_open_f = float(curr["open"])
|
||||
d_long = abs(long_trigger - c_open_f)
|
||||
d_short = abs(short_trigger - c_open_f)
|
||||
direction = "short" if d_short <= d_long else "long"
|
||||
trigger_price = long_trigger if direction == "long" else short_trigger
|
||||
elif short_triggered:
|
||||
direction = "short"
|
||||
trigger_price = short_trigger
|
||||
elif long_triggered:
|
||||
direction = "long"
|
||||
trigger_price = long_trigger
|
||||
|
||||
if direction is None:
|
||||
return None, None, None, None
|
||||
if (
|
||||
self.last_trigger_kline_id == curr_kline_id
|
||||
and self.last_trigger_direction == direction
|
||||
):
|
||||
return None, None, None, None
|
||||
|
||||
return direction, trigger_price, prev, curr
|
||||
|
||||
# ==================== BitMart API ====================
|
||||
|
||||
def get_klines(self):
|
||||
try:
|
||||
end_time = int(time.time())
|
||||
response = self.contractAPI.get_kline(
|
||||
contract_symbol=self.contract_symbol,
|
||||
step=self.kline_step,
|
||||
start_time=end_time - 3600 * 3,
|
||||
end_time=end_time,
|
||||
)[0]["data"]
|
||||
formatted = []
|
||||
for k in response:
|
||||
formatted.append(
|
||||
{
|
||||
"id": int(k["timestamp"]),
|
||||
"open": float(k["open_price"]),
|
||||
"high": float(k["high_price"]),
|
||||
"low": float(k["low_price"]),
|
||||
"close": float(k["close_price"]),
|
||||
}
|
||||
)
|
||||
formatted.sort(key=lambda x: x["id"])
|
||||
return formatted
|
||||
except Exception as e:
|
||||
if "429" in str(e) or "too many requests" in str(e).lower():
|
||||
logger.warning(f"API限流,等待60秒: {e}")
|
||||
time.sleep(60)
|
||||
else:
|
||||
logger.error(f"获取K线异常: {e}")
|
||||
self.ding("获取K线异常", error=True)
|
||||
return None
|
||||
|
||||
def get_available_balance(self):
|
||||
try:
|
||||
response = self.contractAPI.get_assets_detail()[0]
|
||||
if response["code"] == 1000:
|
||||
data = response["data"]
|
||||
if isinstance(data, dict):
|
||||
return float(data.get("available_balance", 0))
|
||||
for asset in data:
|
||||
if asset.get("currency") == "USDT":
|
||||
return float(asset.get("available_balance", 0))
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"余额查询异常: {e}")
|
||||
return None
|
||||
|
||||
def get_position_status(self):
|
||||
try:
|
||||
response = self.contractAPI.get_position(
|
||||
contract_symbol=self.contract_symbol
|
||||
)[0]
|
||||
if response["code"] == 1000:
|
||||
positions = response["data"]
|
||||
if not positions:
|
||||
self.start = 0
|
||||
self.open_avg_price = None
|
||||
self.current_amount = None
|
||||
self.position_cross = None
|
||||
return True
|
||||
self.start = 1 if positions[0]["position_type"] == 1 else -1
|
||||
self.open_avg_price = positions[0]["open_avg_price"]
|
||||
self.current_amount = positions[0]["current_amount"]
|
||||
self.position_cross = positions[0]["position_cross"]
|
||||
return True
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"持仓查询异常: {e}")
|
||||
return False
|
||||
|
||||
def set_leverage(self):
|
||||
try:
|
||||
response = self.contractAPI.post_submit_leverage(
|
||||
contract_symbol=self.contract_symbol,
|
||||
leverage=self.leverage,
|
||||
open_type=self.open_type,
|
||||
)[0]
|
||||
if response["code"] == 1000:
|
||||
logger.success(f"全仓 {self.leverage}x 杠杆设置成功")
|
||||
return True
|
||||
logger.error(f"杠杆设置失败: {response}")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"设置杠杆异常: {e}")
|
||||
return False
|
||||
|
||||
# ==================== 浏览器 ====================
|
||||
|
||||
def _open_browser(self):
|
||||
try:
|
||||
bit_port = openBrowser(id=self.bit_id)
|
||||
co = ChromiumOptions()
|
||||
co.set_local_port(port=bit_port)
|
||||
self.page = ChromiumPage(addr_or_opts=co)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def close_extra_tabs(self):
|
||||
try:
|
||||
for idx, tab in enumerate(self.page.get_tabs()):
|
||||
if idx > 0:
|
||||
tab.close()
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def click_safe(self, xpath, sleep=0.5):
|
||||
try:
|
||||
ele = self.page.ele(xpath)
|
||||
if not ele:
|
||||
return False
|
||||
ele.scroll.to_see(center=True)
|
||||
time.sleep(sleep)
|
||||
ele.click(by_js=True)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def 平仓(self):
|
||||
logger.info("执行平仓...")
|
||||
self.click_safe('x://span[normalize-space(text()) ="市价"]')
|
||||
time.sleep(0.5)
|
||||
self.ding("执行平仓操作")
|
||||
|
||||
def 开单(self, marketPriceLongOrder=0, size=None):
|
||||
if size is None or size <= 0:
|
||||
logger.warning("开单金额无效")
|
||||
return False
|
||||
direction_str = "做多" if marketPriceLongOrder == 1 else "做空"
|
||||
logger.info(f"执行{direction_str},金额: {size}")
|
||||
size = max(1, min(25, int(size)))
|
||||
try:
|
||||
self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
self.page.ele('x://*[@id="size_0"]').input(str(size))
|
||||
if marketPriceLongOrder == -1:
|
||||
self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]')
|
||||
else:
|
||||
self.click_safe('x://span[normalize-space(text()) ="买入/做多"]')
|
||||
self.ding(f"执行{direction_str},金额: {size}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"开单异常: {e}")
|
||||
return False
|
||||
|
||||
def ding(self, msg, error=False):
|
||||
prefix = "❌五分之一:" if error else "🔔五分之一:"
|
||||
full_msg = f"{prefix}{msg}"
|
||||
if error:
|
||||
logger.error(msg)
|
||||
for _ in range(3):
|
||||
ding_executor.submit(self._send_ding_safe, full_msg)
|
||||
else:
|
||||
logger.info(msg)
|
||||
ding_executor.submit(self._send_ding_safe, full_msg)
|
||||
|
||||
def _send_ding_safe(self, msg):
|
||||
try:
|
||||
send_dingtalk_message(msg)
|
||||
except Exception as e:
|
||||
logger.warning(f"消息发送失败: {e}")
|
||||
|
||||
def _send_position_message(self, latest_kline):
|
||||
current_price = float(latest_kline["close"])
|
||||
balance = self.get_available_balance()
|
||||
self.balance = balance if balance is not None else 0.0
|
||||
if self.start != 0:
|
||||
open_avg_price = float(self.open_avg_price)
|
||||
current_amount = float(self.current_amount)
|
||||
position_cross = float(getattr(self, "position_cross", 0) or 0)
|
||||
if self.start == 1:
|
||||
unrealized_pnl = current_amount * 0.001 * (
|
||||
current_price - open_avg_price
|
||||
)
|
||||
else:
|
||||
unrealized_pnl = current_amount * 0.001 * (
|
||||
open_avg_price - current_price
|
||||
)
|
||||
pnl_rate = (
|
||||
(current_price - open_avg_price) / open_avg_price * 100
|
||||
if self.start == 1
|
||||
else (open_avg_price - current_price) / open_avg_price * 100
|
||||
)
|
||||
direction_str = "空" if self.start == -1 else "多"
|
||||
msg = (
|
||||
f"【五分之一 {self.contract_symbol}】\n"
|
||||
f"方向:{direction_str}\n"
|
||||
f"现价:{current_price:.2f}\n"
|
||||
f"开仓均价:{open_avg_price:.2f}\n"
|
||||
f"浮动盈亏:{unrealized_pnl:+.2f} USDT ({pnl_rate:+.2f}%)\n"
|
||||
f"余额:{self.balance:.2f}"
|
||||
)
|
||||
else:
|
||||
msg = (
|
||||
f"【五分之一 {self.contract_symbol}】\n"
|
||||
f"方向:无\n"
|
||||
f"现价:{current_price:.2f}\n"
|
||||
f"余额:{self.balance:.2f}"
|
||||
)
|
||||
self.ding(msg)
|
||||
|
||||
# ==================== 主循环 ====================
|
||||
|
||||
def action(self):
|
||||
if not self.set_leverage():
|
||||
logger.error("杠杆设置失败")
|
||||
return
|
||||
if not self._open_browser():
|
||||
self.ding("打开浏览器失败!", error=True)
|
||||
return
|
||||
logger.info("浏览器打开成功")
|
||||
self.close_extra_tabs()
|
||||
self.page.get("https://derivatives.bitmart.com/zh-CN/futures/ETHUSDT")
|
||||
time.sleep(2)
|
||||
self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
logger.info(
|
||||
f"五分之一策略(3分钟K线)开始监测,间隔: {self.check_interval}秒"
|
||||
)
|
||||
|
||||
last_report_time = 0
|
||||
report_interval = 300
|
||||
|
||||
while True:
|
||||
for _ in range(5):
|
||||
if self._open_browser():
|
||||
break
|
||||
time.sleep(5)
|
||||
else:
|
||||
self.ding("打开浏览器失败!", error=True)
|
||||
return
|
||||
self.close_extra_tabs()
|
||||
self.page.get("https://derivatives.bitmart.com/zh-CN/futures/ETHUSDT")
|
||||
time.sleep(2)
|
||||
self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
|
||||
try:
|
||||
kline_data = self.get_klines()
|
||||
if not kline_data or len(kline_data) < 3:
|
||||
logger.warning("K线数据不足...")
|
||||
time.sleep(self.check_interval)
|
||||
continue
|
||||
|
||||
curr = kline_data[-1]
|
||||
if not self.get_position_status():
|
||||
logger.warning("获取仓位失败,使用缓存")
|
||||
|
||||
direction, trigger_price, valid_prev, curr_kline = (
|
||||
self.check_trigger(kline_data)
|
||||
)
|
||||
|
||||
if direction:
|
||||
curr_kline_id = curr_kline["id"]
|
||||
if self.last_trade_kline_id == curr_kline_id:
|
||||
self.last_trigger_kline_id = curr_kline_id
|
||||
self.last_trigger_direction = direction
|
||||
time.sleep(self.check_interval)
|
||||
continue
|
||||
|
||||
if (direction == "long" and self.start == 1) or (
|
||||
direction == "short" and self.start == -1
|
||||
):
|
||||
self.last_trigger_kline_id = curr_kline_id
|
||||
self.last_trigger_direction = direction
|
||||
time.sleep(self.check_interval)
|
||||
continue
|
||||
|
||||
balance = self.get_available_balance()
|
||||
trade_size = (balance or 0) * self.risk_percent
|
||||
executed = False
|
||||
|
||||
if direction == "long":
|
||||
if self.start == -1:
|
||||
self.平仓()
|
||||
time.sleep(1)
|
||||
self.开单(marketPriceLongOrder=1, size=trade_size)
|
||||
executed = True
|
||||
elif self.start == 0:
|
||||
self.开单(marketPriceLongOrder=1, size=trade_size)
|
||||
executed = True
|
||||
elif direction == "short":
|
||||
if self.start == 1:
|
||||
self.平仓()
|
||||
time.sleep(1)
|
||||
self.开单(marketPriceLongOrder=-1, size=trade_size)
|
||||
executed = True
|
||||
elif self.start == 0:
|
||||
self.开单(marketPriceLongOrder=-1, size=trade_size)
|
||||
executed = True
|
||||
|
||||
self.last_trigger_kline_id = curr_kline_id
|
||||
self.last_trigger_direction = direction
|
||||
if executed:
|
||||
self.last_trade_kline_id = curr_kline_id
|
||||
self.get_position_status()
|
||||
self._send_position_message(curr_kline)
|
||||
last_report_time = time.time()
|
||||
|
||||
if time.time() - last_report_time >= report_interval:
|
||||
if self.get_position_status():
|
||||
self._send_position_message(kline_data[-1])
|
||||
last_report_time = time.time()
|
||||
|
||||
time.sleep(self.check_interval)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"主循环异常: {e}")
|
||||
time.sleep(self.check_interval)
|
||||
time.sleep(3)
|
||||
if random.randint(1, 10) > 7:
|
||||
self.page.close()
|
||||
time.sleep(15)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
OpenBasedFifthStrategy(bit_id=BIT_ID).action()
|
||||
except KeyboardInterrupt:
|
||||
logger.info("程序被用户中断")
|
||||
finally:
|
||||
ding_executor.shutdown(wait=True)
|
||||
logger.info("已退出")
|
||||
Binary file not shown.
@@ -31,10 +31,10 @@ LOCAL_TZ = ZoneInfo("America/New_York")
|
||||
# 代理(可选)
|
||||
PROXY = {
|
||||
'proxy_type': "socks5",
|
||||
'addr': "202.155.144.102",
|
||||
'port': 31102,
|
||||
'username': "SyNuejCtrQ",
|
||||
'password': "MH8ioL7EXf"
|
||||
'addr': "199.168.137.123",
|
||||
'port': 12345,
|
||||
'username': "haha",
|
||||
'password': "haha"
|
||||
}
|
||||
|
||||
INVITE_LINK = "https://www.websea.my/en/signup?key=77346588" # 请填入您的邀请链接
|
||||
@@ -134,6 +134,7 @@ main_buttons = [
|
||||
[Button.inline("Jemputan Saya", command_payload("my_invites")),
|
||||
Button.inline("Harga Koin", command_payload("btc"))],
|
||||
[Button.inline("Mata Saya", command_payload("points")), Button.inline("Bantuan", command_payload("help"))],
|
||||
[Button.url("Official Customer Service", "https://t.me/uogrrr")],
|
||||
]
|
||||
|
||||
|
||||
@@ -514,6 +515,7 @@ def get_crypto_buttons():
|
||||
if row:
|
||||
buttons.append(row)
|
||||
buttons.append([Button.inline("Kembali ke Menu", command_payload("help"))])
|
||||
buttons.append([Button.url("Official Customer Service", "https://t.me/uogrrr")])
|
||||
return buttons
|
||||
|
||||
|
||||
|
||||
77
test.py
77
test.py
@@ -1,3 +1,76 @@
|
||||
from time import sleep
|
||||
from datetime import datetime
|
||||
import random
|
||||
|
||||
from pathlib import Path
|
||||
print(Path(__file__).parent)
|
||||
from rich.console import Console
|
||||
from rich.panel import Panel
|
||||
from rich.table import Table
|
||||
from rich.layout import Layout
|
||||
from rich.align import Align
|
||||
from rich.text import Text
|
||||
from rich.live import Live
|
||||
|
||||
console = Console()
|
||||
|
||||
def make_header() -> Panel:
|
||||
title = Text("SYSTEM DASHBOARD", style="bold cyan")
|
||||
subtitle = Text(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), style="dim")
|
||||
return Panel(Align.center(title + "\n" + subtitle), border_style="cyan")
|
||||
|
||||
def make_metrics_table(cpu: int, mem: int, qps: int) -> Panel:
|
||||
t = Table(show_header=True, header_style="bold magenta", expand=True)
|
||||
t.add_column("Metric")
|
||||
t.add_column("Value", justify="right")
|
||||
t.add_row("CPU", f"{cpu}%")
|
||||
t.add_row("Memory", f"{mem}%")
|
||||
t.add_row("QPS", str(qps))
|
||||
return Panel(t, title="Metrics", border_style="magenta")
|
||||
|
||||
def make_status_panel(status: str, logs: list[str]) -> Panel:
|
||||
body = "\n".join(logs[-8:]) if logs else "No logs yet."
|
||||
text = Text(f"[bold]Status:[/bold] {status}\n\n", style="white")
|
||||
text.append(body, style="dim")
|
||||
return Panel(text, title="Status / Logs", border_style="green")
|
||||
|
||||
def make_footer(msg: str) -> Panel:
|
||||
return Panel(Text(msg, style="bold yellow"), border_style="yellow")
|
||||
|
||||
def build_layout(cpu: int, mem: int, qps: int, status: str, logs: list[str]) -> Layout:
|
||||
layout = Layout()
|
||||
layout.split_column(
|
||||
Layout(make_header(), name="header", size=5),
|
||||
Layout(name="body", ratio=1),
|
||||
Layout(make_footer("Press Ctrl+C to exit"), name="footer", size=3),
|
||||
)
|
||||
layout["body"].split_row(
|
||||
Layout(make_metrics_table(cpu, mem, qps), name="left", ratio=1),
|
||||
Layout(make_status_panel(status, logs), name="right", ratio=2),
|
||||
)
|
||||
return layout
|
||||
|
||||
def main():
|
||||
logs = []
|
||||
status = "OK"
|
||||
with Live(console=console, refresh_per_second=8, screen=True):
|
||||
while True:
|
||||
cpu = random.randint(1, 100)
|
||||
mem = random.randint(1, 100)
|
||||
qps = random.randint(50, 5000)
|
||||
|
||||
if cpu > 85 or mem > 90:
|
||||
status = "WARN"
|
||||
logs.append(f"{datetime.now().strftime('%H:%M:%S')} - High load detected")
|
||||
else:
|
||||
status = "OK"
|
||||
if random.random() < 0.2:
|
||||
logs.append(f"{datetime.now().strftime('%H:%M:%S')} - Heartbeat")
|
||||
|
||||
layout = build_layout(cpu, mem, qps, status, logs)
|
||||
Live.get_renderable = lambda self: layout # 小技巧:避免重复创建 Live
|
||||
sleep(0.15)
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
main()
|
||||
except KeyboardInterrupt:
|
||||
console.print("\nBye!", style="bold red")
|
||||
|
||||
949
test2.py
949
test2.py
@@ -1,949 +0,0 @@
|
||||
import os
|
||||
import time
|
||||
import uuid
|
||||
import datetime
|
||||
from dataclasses import dataclass
|
||||
|
||||
from tqdm import tqdm
|
||||
from loguru import logger
|
||||
|
||||
from bitmart.api_contract import APIContract
|
||||
from bitmart.lib.cloud_exceptions import APIException
|
||||
|
||||
from 交易.tools import send_dingtalk_message
|
||||
|
||||
|
||||
@dataclass
|
||||
class StrategyConfig:
|
||||
# =============================
|
||||
# 1m | ETH 永续 | 控止损≤5/日
|
||||
# =============================
|
||||
|
||||
# ===== 合约 =====
|
||||
contract_symbol: str = "ETHUSDT"
|
||||
open_type: str = "cross"
|
||||
leverage: str = "30"
|
||||
|
||||
# ===== K线与指标 =====
|
||||
step_min: int = 1
|
||||
lookback_min: int = 240
|
||||
ema_len: int = 36
|
||||
atr_len: int = 14
|
||||
|
||||
# ===== ADX 趋势过滤(新增)=====
|
||||
# 目的:单边趋势(ADX高)时,抑制/禁止逆势均值回归单,避免反复反向开仓止损
|
||||
enable_adx_filter: bool = True
|
||||
adx_len: int = 14
|
||||
adx_threshold: float = 25.0 # 常用:20~30区间,你可按回测调整
|
||||
# 过滤模式:
|
||||
# - "block_countertrend": 只禁止逆着 DI 的方向开仓(推荐,既防反手又不完全停机)
|
||||
# - "block_all": ADX 高时直接不允许任何新开仓(更保守)
|
||||
adx_mode: str = "block_countertrend"
|
||||
# 趋势保护冷却:当 ADX 高且刚止损,延长冷却,减少“止损->立刻反手”的连环
|
||||
cooldown_sec_after_sl_extra: int = 40
|
||||
|
||||
# =========================================================
|
||||
# ✅ 自动阈值:ATR/Price 分位数基准(更稳,不被短时噪声带跑)
|
||||
# =========================================================
|
||||
vol_baseline_window: int = 60
|
||||
vol_baseline_quantile: float = 0.65
|
||||
vol_scale_min: float = 0.80
|
||||
vol_scale_max: float = 1.60
|
||||
|
||||
# ✅ baseline 每 60 秒刷新一次(体感更明显、也省CPU)
|
||||
base_ratio_refresh_sec: int = 180
|
||||
|
||||
# =========================================================
|
||||
# ✅ 动态 floor(方案一)
|
||||
# floor = clamp(min, base_k * base_ratio, max)
|
||||
# 目的:跟着典型波动变,过滤小噪声;tp/sl 也随环境自适应
|
||||
# =========================================================
|
||||
# entry_dev_floor 动态
|
||||
entry_dev_floor_min: float = 0.0012 # 0.12%
|
||||
entry_dev_floor_max: float = 0.0030 # 0.30%
|
||||
entry_dev_floor_base_k: float = 1.10 # entry_floor = 1.10 * base_ratio
|
||||
|
||||
# tp_floor 动态
|
||||
tp_floor_min: float = 0.0006 # 0.06%
|
||||
tp_floor_max: float = 0.0020 # 0.20%
|
||||
tp_floor_base_k: float = 0.55 # tp_floor = 0.55 * base_ratio
|
||||
|
||||
# sl_floor 动态
|
||||
sl_floor_min: float = 0.0018 # 0.18%
|
||||
sl_floor_max: float = 0.0060 # 0.60%
|
||||
sl_floor_base_k: float = 1.35 # sl_floor = 1.35 * base_ratio
|
||||
|
||||
# =========================================================
|
||||
# ✅ 动态阈值倍率
|
||||
# =========================================================
|
||||
entry_k: float = 1.45
|
||||
tp_k: float = 0.65
|
||||
sl_k: float = 1.05
|
||||
|
||||
# ===== 时间/冷却 =====
|
||||
max_hold_sec: int = 75
|
||||
cooldown_sec_after_exit: int = 20
|
||||
|
||||
# ===== 下单/仓位 =====
|
||||
risk_percent: float = 0.004
|
||||
min_size: int = 1
|
||||
max_size: int = 5000
|
||||
|
||||
# ===== 日内风控 =====
|
||||
daily_loss_limit: float = 0.02
|
||||
daily_profit_cap: float = 0.01
|
||||
|
||||
# ===== 危险模式过滤 =====
|
||||
atr_ratio_kill: float = 0.0038
|
||||
big_body_kill: float = 0.010
|
||||
|
||||
# ===== 轮询节奏 =====
|
||||
klines_refresh_sec: int = 10
|
||||
tick_refresh_sec: int = 1
|
||||
status_notify_sec: int = 60
|
||||
|
||||
# =========================================================
|
||||
# ✅ 止损后同向入场加门槛(你原来的逻辑保留)
|
||||
# =========================================================
|
||||
reentry_penalty_mult: float = 1.55
|
||||
reentry_penalty_max_sec: int = 180
|
||||
reset_band_k: float = 0.45
|
||||
reset_band_floor: float = 0.0006
|
||||
|
||||
# =========================================================
|
||||
# ✅ 止损后同方向 SL 放宽幅度与"止损时 vol_scale"联动
|
||||
# =========================================================
|
||||
post_sl_sl_max_sec: int = 90
|
||||
post_sl_mult_min: float = 1.02
|
||||
post_sl_mult_max: float = 1.16
|
||||
post_sl_vol_alpha: float = 0.20
|
||||
|
||||
|
||||
class BitmartFuturesMeanReversionBot:
|
||||
def __init__(self, cfg: StrategyConfig):
|
||||
self.cfg = cfg
|
||||
|
||||
self.api_key = os.getenv("BITMART_API_KEY", "").strip()
|
||||
self.secret_key = os.getenv("BITMART_SECRET_KEY", "").strip()
|
||||
self.memo = os.getenv("BITMART_MEMO", "合约交易").strip()
|
||||
|
||||
if not self.api_key or not self.secret_key:
|
||||
raise RuntimeError("请先设置环境变量 BITMART_API_KEY / BITMART_SECRET_KEY / BITMART_MEMO(可选)")
|
||||
|
||||
self.contractAPI = APIContract(self.api_key, self.secret_key, self.memo, timeout=(5, 15))
|
||||
|
||||
# 持仓状态: -1 空, 0 无, 1 多
|
||||
self.pos = 0
|
||||
self.entry_price = None
|
||||
self.entry_ts = None
|
||||
self.last_exit_ts = 0
|
||||
|
||||
# 日内权益基准
|
||||
self.day_start_equity = None
|
||||
self.trading_enabled = True
|
||||
self.day_tag = datetime.date.today()
|
||||
|
||||
# 缓存
|
||||
self._klines_cache = None
|
||||
self._klines_cache_ts = 0
|
||||
self._last_status_notify_ts = 0
|
||||
|
||||
# ✅ base_ratio 缓存
|
||||
self._base_ratio_cached = 0.0015 # 初始化默认值 0.15%
|
||||
self._base_ratio_ts = 0.0
|
||||
|
||||
# ✅ 止损后"同向入场加门槛"状态
|
||||
self.last_sl_dir = 0 # 1=多止损,-1=空止损,0=无
|
||||
self.last_sl_ts = 0.0
|
||||
|
||||
# ✅ 止损后"同方向 SL 联动放宽"状态
|
||||
self.post_sl_dir = 0
|
||||
self.post_sl_ts = 0.0
|
||||
self.post_sl_vol_scale = 1.0 # 记录止损时的 vol_scale
|
||||
|
||||
self.pbar = tqdm(total=60, desc="运行中(秒)", ncols=90)
|
||||
logger.info(f"初始化完成,基准波动率默认值: {self._base_ratio_cached * 100:.4f}%")
|
||||
|
||||
# ----------------- 通用工具 -----------------
|
||||
def ding(self, msg, error=False):
|
||||
prefix = "❌bitmart:" if error else "🔔bitmart:"
|
||||
if error:
|
||||
for _ in range(3):
|
||||
send_dingtalk_message(f"{prefix}{msg}")
|
||||
else:
|
||||
send_dingtalk_message(f"{prefix}{msg}")
|
||||
|
||||
def set_leverage(self) -> bool:
|
||||
try:
|
||||
resp = self.contractAPI.post_submit_leverage(
|
||||
contract_symbol=self.cfg.contract_symbol,
|
||||
leverage=self.cfg.leverage,
|
||||
open_type=self.cfg.open_type
|
||||
)[0]
|
||||
if resp.get("code") == 1000:
|
||||
logger.success(f"设置杠杆成功:{self.cfg.open_type} + {self.cfg.leverage}x")
|
||||
return True
|
||||
logger.error(f"设置杠杆失败: {resp}")
|
||||
self.ding(f"设置杠杆失败: {resp}", error=True)
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"设置杠杆异常: {e}")
|
||||
self.ding(f"设置杠杆异常: {e}", error=True)
|
||||
return False
|
||||
|
||||
# ----------------- 行情/指标 -----------------
|
||||
def get_klines_cached(self):
|
||||
now = time.time()
|
||||
if self._klines_cache is not None and (now - self._klines_cache_ts) < self.cfg.klines_refresh_sec:
|
||||
return self._klines_cache
|
||||
|
||||
kl = self.get_klines()
|
||||
if kl:
|
||||
self._klines_cache = kl
|
||||
self._klines_cache_ts = now
|
||||
return self._klines_cache
|
||||
|
||||
def get_klines(self):
|
||||
try:
|
||||
end_time = int(time.time())
|
||||
start_time = end_time - 60 * self.cfg.lookback_min
|
||||
|
||||
resp = self.contractAPI.get_kline(
|
||||
contract_symbol=self.cfg.contract_symbol,
|
||||
step=self.cfg.step_min,
|
||||
start_time=start_time,
|
||||
end_time=end_time
|
||||
)[0]
|
||||
|
||||
if resp.get("code") != 1000:
|
||||
logger.error(f"获取K线失败: {resp}")
|
||||
return None
|
||||
|
||||
data = resp.get("data", [])
|
||||
formatted = []
|
||||
for k in data:
|
||||
formatted.append({
|
||||
"id": int(k["timestamp"]),
|
||||
"open": float(k["open_price"]),
|
||||
"high": float(k["high_price"]),
|
||||
"low": float(k["low_price"]),
|
||||
"close": float(k["close_price"]),
|
||||
})
|
||||
formatted.sort(key=lambda x: x["id"])
|
||||
return formatted
|
||||
except Exception as e:
|
||||
logger.error(f"获取K线异常: {e}")
|
||||
self.ding(f"获取K线异常: {e}", error=True)
|
||||
return None
|
||||
|
||||
def get_last_price(self, fallback_close: float) -> float:
|
||||
try:
|
||||
if hasattr(self.contractAPI, "get_contract_details"):
|
||||
r = self.contractAPI.get_contract_details(contract_symbol=self.cfg.contract_symbol)[0]
|
||||
d = r.get("data") if isinstance(r, dict) else None
|
||||
if isinstance(d, dict):
|
||||
for key in ("last_price", "mark_price", "index_price"):
|
||||
if key in d and d[key] is not None:
|
||||
return float(d[key])
|
||||
|
||||
if hasattr(self.contractAPI, "get_ticker"):
|
||||
r = self.contractAPI.get_ticker(contract_symbol=self.cfg.contract_symbol)[0]
|
||||
d = r.get("data") if isinstance(r, dict) else None
|
||||
if isinstance(d, dict):
|
||||
for key in ("last_price", "price", "last", "close"):
|
||||
if key in d and d[key] is not None:
|
||||
return float(d[key])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return float(fallback_close)
|
||||
|
||||
@staticmethod
|
||||
def ema(values, n: int) -> float:
|
||||
k = 2 / (n + 1)
|
||||
e = values[0]
|
||||
for v in values[1:]:
|
||||
e = v * k + e * (1 - k)
|
||||
return e
|
||||
|
||||
@staticmethod
|
||||
def atr(klines, n: int) -> float:
|
||||
if len(klines) < n + 1:
|
||||
return 0.0
|
||||
trs = []
|
||||
for i in range(-n, 0):
|
||||
cur = klines[i]
|
||||
prev = klines[i - 1]
|
||||
tr = max(
|
||||
cur["high"] - cur["low"],
|
||||
abs(cur["high"] - prev["close"]),
|
||||
abs(cur["low"] - prev["close"]),
|
||||
)
|
||||
trs.append(tr)
|
||||
return sum(trs) / len(trs)
|
||||
|
||||
@staticmethod
|
||||
def _wilder_smooth(prev: float, val: float, n: int) -> float:
|
||||
# Wilder smoothing: prev - prev/n + val
|
||||
return prev - (prev / n) + val
|
||||
|
||||
def adx(self, klines, n: int):
|
||||
"""
|
||||
返回 (adx, plus_di, minus_di)
|
||||
采用经典 Wilder ADX/DI 计算(足够稳,避免趋势期逆势反复开仓)
|
||||
"""
|
||||
if len(klines) < (2 * n + 2):
|
||||
return 0.0, 0.0, 0.0
|
||||
|
||||
highs = [k["high"] for k in klines]
|
||||
lows = [k["low"] for k in klines]
|
||||
closes = [k["close"] for k in klines]
|
||||
|
||||
# 计算 TR, +DM, -DM 序列(从1开始)
|
||||
tr_list = []
|
||||
pdm_list = []
|
||||
mdm_list = []
|
||||
|
||||
for i in range(1, len(klines)):
|
||||
high = highs[i]
|
||||
low = lows[i]
|
||||
prev_close = closes[i - 1]
|
||||
prev_high = highs[i - 1]
|
||||
prev_low = lows[i - 1]
|
||||
|
||||
tr = max(high - low, abs(high - prev_close), abs(low - prev_close))
|
||||
up_move = high - prev_high
|
||||
down_move = prev_low - low
|
||||
|
||||
pdm = up_move if (up_move > down_move and up_move > 0) else 0.0
|
||||
mdm = down_move if (down_move > up_move and down_move > 0) else 0.0
|
||||
|
||||
tr_list.append(tr)
|
||||
pdm_list.append(pdm)
|
||||
mdm_list.append(mdm)
|
||||
|
||||
# 初始平滑值(第一个n段的和)
|
||||
tr14 = sum(tr_list[:n])
|
||||
pdm14 = sum(pdm_list[:n])
|
||||
mdm14 = sum(mdm_list[:n])
|
||||
|
||||
def safe_div(a, b):
|
||||
return (a / b) if b != 0 else 0.0
|
||||
|
||||
plus_di = 100.0 * safe_div(pdm14, tr14)
|
||||
minus_di = 100.0 * safe_div(mdm14, tr14)
|
||||
dx = 100.0 * safe_div(abs(plus_di - minus_di), (plus_di + minus_di))
|
||||
dx_list = [dx]
|
||||
|
||||
# 继续平滑并计算后续 DX
|
||||
for i in range(n, len(tr_list)):
|
||||
tr14 = self._wilder_smooth(tr14, tr_list[i], n)
|
||||
pdm14 = self._wilder_smooth(pdm14, pdm_list[i], n)
|
||||
mdm14 = self._wilder_smooth(mdm14, mdm_list[i], n)
|
||||
|
||||
plus_di = 100.0 * safe_div(pdm14, tr14)
|
||||
minus_di = 100.0 * safe_div(mdm14, tr14)
|
||||
dx = 100.0 * safe_div(abs(plus_di - minus_di), (plus_di + minus_di))
|
||||
dx_list.append(dx)
|
||||
|
||||
# ADX 是 DX 的 Wilder 平滑,常见做法:先取前 n 个 DX 的均值作为初值
|
||||
if len(dx_list) < (n + 1):
|
||||
return 0.0, plus_di, minus_di
|
||||
|
||||
adx0 = sum(dx_list[:n]) / n
|
||||
adx_val = adx0
|
||||
for j in range(n, len(dx_list)):
|
||||
adx_val = (adx_val * (n - 1) + dx_list[j]) / n
|
||||
|
||||
return float(adx_val), float(plus_di), float(minus_di)
|
||||
|
||||
def is_danger_market(self, klines, price: float) -> bool:
|
||||
last = klines[-1]
|
||||
body = abs(last["close"] - last["open"]) / last["open"] if last["open"] else 0.0
|
||||
if body >= self.cfg.big_body_kill:
|
||||
return True
|
||||
|
||||
a = self.atr(klines, self.cfg.atr_len)
|
||||
atr_ratio = (a / price) if price > 0 else 0.0
|
||||
if atr_ratio >= self.cfg.atr_ratio_kill:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def atr_ratio_baseline(self, klines) -> float:
|
||||
"""简化版ATR基准计算"""
|
||||
window = min(self.cfg.vol_baseline_window, len(klines) - self.cfg.atr_len - 1)
|
||||
if window <= 10:
|
||||
logger.warning(f"数据不足计算基准: {len(klines)}根K线")
|
||||
return 0.0
|
||||
|
||||
ratios = []
|
||||
step = 3
|
||||
for i in range(-window, 0, step):
|
||||
if len(klines) + i < self.cfg.atr_len + 1:
|
||||
continue
|
||||
|
||||
start_idx = len(klines) + i - self.cfg.atr_len
|
||||
end_idx = len(klines) + i
|
||||
if start_idx < 0 or end_idx <= start_idx:
|
||||
continue
|
||||
|
||||
sub_klines = klines[start_idx:end_idx]
|
||||
if len(sub_klines) >= self.cfg.atr_len + 1:
|
||||
a = self.atr(sub_klines, self.cfg.atr_len)
|
||||
price = klines[end_idx - 1]["close"]
|
||||
if a > 0 and price > 0:
|
||||
ratio = a / price
|
||||
if 0.0001 < ratio < 0.01:
|
||||
ratios.append(ratio)
|
||||
|
||||
if len(ratios) < 5:
|
||||
a = self.atr(klines[-60:], self.cfg.atr_len)
|
||||
price = klines[-1]["close"]
|
||||
if a > 0 and price > 0:
|
||||
baseline = a / price
|
||||
logger.debug(f"使用全量数据计算基准: {baseline * 100:.4f}%")
|
||||
return baseline
|
||||
return 0.0
|
||||
|
||||
ratios.sort()
|
||||
idx = min(len(ratios) - 1, max(0, int(self.cfg.vol_baseline_quantile * (len(ratios) - 1))))
|
||||
baseline = ratios[idx]
|
||||
logger.debug(
|
||||
f"基准计算: 样本数={len(ratios)}, 基准={baseline * 100:.4f}%, "
|
||||
f"范围=[{ratios[0] * 100:.4f}%, {ratios[-1] * 100:.4f}%]"
|
||||
)
|
||||
return baseline
|
||||
|
||||
def get_base_ratio_cached(self, klines) -> float:
|
||||
"""获取缓存的基准波动率,定期刷新"""
|
||||
now = time.time()
|
||||
refresh_sec = self.cfg.base_ratio_refresh_sec
|
||||
|
||||
if (self._base_ratio_cached is None or (now - self._base_ratio_ts) >= refresh_sec):
|
||||
baseline = self.atr_ratio_baseline(klines)
|
||||
if baseline > 0.0001:
|
||||
self._base_ratio_cached = baseline
|
||||
self._base_ratio_ts = now
|
||||
logger.info(f"基准波动率更新: {baseline * 100:.4f}%")
|
||||
else:
|
||||
current_price = klines[-1]["close"] if klines else 3000
|
||||
if current_price > 4000:
|
||||
default_baseline = 0.0010
|
||||
elif current_price > 3500:
|
||||
default_baseline = 0.0012
|
||||
elif current_price > 3000:
|
||||
default_baseline = 0.0015
|
||||
elif current_price > 2500:
|
||||
default_baseline = 0.0018
|
||||
else:
|
||||
default_baseline = 0.0020
|
||||
|
||||
self._base_ratio_cached = default_baseline
|
||||
self._base_ratio_ts = now
|
||||
logger.warning(
|
||||
f"使用价格动态默认基准: {default_baseline * 100:.4f}% (价格=${current_price:.0f})"
|
||||
)
|
||||
|
||||
return self._base_ratio_cached
|
||||
|
||||
@staticmethod
|
||||
def _clamp(x: float, lo: float, hi: float) -> float:
|
||||
return max(lo, min(hi, x))
|
||||
|
||||
def dynamic_thresholds(self, atr_ratio: float, base_ratio: float):
|
||||
"""
|
||||
✅ entry/tp/sl 全部动态(修复版):
|
||||
- vol_scale:atr_ratio/base_ratio 限幅
|
||||
- floor:方案一 (floor = clamp(min, k*base_ratio, max))
|
||||
- 最终阈值:max(floor, k * vol_scale * atr_ratio)
|
||||
"""
|
||||
if atr_ratio <= 0:
|
||||
logger.warning(f"ATR比率异常: {atr_ratio}")
|
||||
atr_ratio = 0.001
|
||||
|
||||
if base_ratio < 0.0005:
|
||||
base_ratio = max(0.001, atr_ratio * 1.2)
|
||||
logger.debug(f"基准太小,使用调整后的atr_ratio: {base_ratio * 100:.4f}%")
|
||||
|
||||
if base_ratio > 0:
|
||||
raw_scale = atr_ratio / base_ratio
|
||||
vol_scale = self._clamp(raw_scale, self.cfg.vol_scale_min, self.cfg.vol_scale_max)
|
||||
logger.debug(
|
||||
f"vol_scale: {raw_scale:.2f} → {vol_scale:.2f} (atr={atr_ratio * 100:.3f}%, base={base_ratio * 100:.3f}%)"
|
||||
)
|
||||
else:
|
||||
vol_scale = 1.0
|
||||
logger.warning("基准无效,使用默认vol_scale=1.0")
|
||||
|
||||
entry_floor_raw = self.cfg.entry_dev_floor_base_k * base_ratio
|
||||
entry_floor = self._clamp(entry_floor_raw, self.cfg.entry_dev_floor_min, self.cfg.entry_dev_floor_max)
|
||||
|
||||
tp_floor_raw = self.cfg.tp_floor_base_k * base_ratio
|
||||
tp_floor = self._clamp(tp_floor_raw, self.cfg.tp_floor_min, self.cfg.tp_floor_max)
|
||||
|
||||
sl_floor_raw = self.cfg.sl_floor_base_k * base_ratio
|
||||
sl_floor = self._clamp(sl_floor_raw, self.cfg.sl_floor_min, self.cfg.sl_floor_max)
|
||||
|
||||
entry_dev_atr_part = self.cfg.entry_k * vol_scale * atr_ratio
|
||||
entry_dev = max(entry_floor, entry_dev_atr_part)
|
||||
|
||||
tp_atr_part = self.cfg.tp_k * vol_scale * atr_ratio
|
||||
tp = max(tp_floor, tp_atr_part)
|
||||
|
||||
sl_atr_part = self.cfg.sl_k * vol_scale * atr_ratio
|
||||
sl = max(sl_floor, sl_atr_part)
|
||||
|
||||
entry_dev = max(entry_dev, self.cfg.entry_dev_floor_min)
|
||||
|
||||
logger.info(
|
||||
f"动态阈值: atr={atr_ratio * 100:.4f}%, base={base_ratio * 100:.4f}%, "
|
||||
f"vol_scale={vol_scale:.2f}, floor={entry_floor * 100:.4f}%, "
|
||||
f"atr_part={entry_dev_atr_part * 100:.4f}%, 最终entry_dev={entry_dev * 100:.4f}%"
|
||||
)
|
||||
|
||||
return entry_dev, tp, sl, vol_scale, entry_floor, tp_floor, sl_floor
|
||||
|
||||
# ----------------- 账户/仓位 -----------------
|
||||
def get_assets_available(self) -> float:
|
||||
try:
|
||||
resp = self.contractAPI.get_assets_detail()[0]
|
||||
if resp.get("code") != 1000:
|
||||
return 0.0
|
||||
data = resp.get("data")
|
||||
if isinstance(data, dict):
|
||||
return float(data.get("available_balance", 0))
|
||||
if isinstance(data, list):
|
||||
for asset in data:
|
||||
if asset.get("currency") == "USDT":
|
||||
return float(asset.get("available_balance", 0))
|
||||
return 0.0
|
||||
except Exception as e:
|
||||
logger.error(f"余额查询异常: {e}")
|
||||
return 0.0
|
||||
|
||||
def get_position_status(self) -> bool:
|
||||
try:
|
||||
resp = self.contractAPI.get_position(contract_symbol=self.cfg.contract_symbol)[0]
|
||||
if resp.get("code") != 1000:
|
||||
return False
|
||||
|
||||
positions = resp.get("data", [])
|
||||
if not positions:
|
||||
self.pos = 0
|
||||
return True
|
||||
|
||||
p = positions[0]
|
||||
self.pos = 1 if p["position_type"] == 1 else -1
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"持仓查询异常: {e}")
|
||||
self.ding(f"持仓查询异常: {e}", error=True)
|
||||
return False
|
||||
|
||||
def get_equity_proxy(self) -> float:
|
||||
return self.get_assets_available()
|
||||
|
||||
def refresh_daily_baseline(self):
|
||||
today = datetime.date.today()
|
||||
if today != self.day_tag:
|
||||
self.day_tag = today
|
||||
self.day_start_equity = None
|
||||
self.trading_enabled = True
|
||||
self.ding(f"新的一天({today}):重置日内风控基准")
|
||||
|
||||
def risk_kill_switch(self):
|
||||
self.refresh_daily_baseline()
|
||||
equity = self.get_equity_proxy()
|
||||
if equity <= 0:
|
||||
return
|
||||
|
||||
if self.day_start_equity is None:
|
||||
self.day_start_equity = equity
|
||||
logger.info(f"日内权益基准设定:{equity:.2f} USDT")
|
||||
return
|
||||
|
||||
pnl = (equity - self.day_start_equity) / self.day_start_equity
|
||||
if pnl <= -self.cfg.daily_loss_limit:
|
||||
self.trading_enabled = False
|
||||
self.ding(f"触发日止损:{pnl * 100:.2f}% -> 停机", error=True)
|
||||
|
||||
if pnl >= self.cfg.daily_profit_cap:
|
||||
self.trading_enabled = False
|
||||
self.ding(f"达到日盈利封顶:{pnl * 100:.2f}% -> 停机")
|
||||
|
||||
# ----------------- 下单 -----------------
|
||||
def calculate_size(self, price: float) -> int:
|
||||
bal = self.get_assets_available()
|
||||
if bal < 10:
|
||||
return 0
|
||||
|
||||
margin = bal * self.cfg.risk_percent
|
||||
lev = int(self.cfg.leverage)
|
||||
|
||||
# ⚠️ 沿用你的原假设:1张≈0.001ETH
|
||||
size = int((margin * lev) / (price * 0.001))
|
||||
size = max(self.cfg.min_size, size)
|
||||
size = min(self.cfg.max_size, size)
|
||||
return size
|
||||
|
||||
def place_market_order(self, side: int, size: int) -> bool:
|
||||
if size <= 0:
|
||||
return False
|
||||
|
||||
client_order_id = f"mr_{int(time.time())}_{uuid.uuid4().hex[:8]}"
|
||||
try:
|
||||
resp = self.contractAPI.post_submit_order(
|
||||
contract_symbol=self.cfg.contract_symbol,
|
||||
client_order_id=client_order_id,
|
||||
side=side,
|
||||
mode=1,
|
||||
type="market",
|
||||
leverage=self.cfg.leverage,
|
||||
open_type=self.cfg.open_type,
|
||||
size=size
|
||||
)[0]
|
||||
|
||||
logger.info(f"order_resp: {resp}")
|
||||
|
||||
if resp.get("code") == 1000:
|
||||
return True
|
||||
|
||||
self.ding(f"下单失败: {resp}", error=True)
|
||||
return False
|
||||
|
||||
except APIException as e:
|
||||
logger.error(f"API下单异常: {e}")
|
||||
self.ding(f"API下单异常: {e}", error=True)
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"下单未知异常: {e}")
|
||||
self.ding(f"下单未知异常: {e}", error=True)
|
||||
return False
|
||||
|
||||
def close_position_all(self):
|
||||
if self.pos == 1:
|
||||
ok = self.place_market_order(3, 999999)
|
||||
if ok:
|
||||
self.pos = 0
|
||||
elif self.pos == -1:
|
||||
ok = self.place_market_order(2, 999999)
|
||||
if ok:
|
||||
self.pos = 0
|
||||
|
||||
# ----------------- 止损后机制 -----------------
|
||||
def _reentry_penalty_active(self, dev: float, entry_dev: float) -> bool:
|
||||
"""检查是否需要应用重新入场惩罚(你原逻辑保留)"""
|
||||
if self.last_sl_dir == 0:
|
||||
return False
|
||||
|
||||
if (time.time() - self.last_sl_ts) > self.cfg.reentry_penalty_max_sec:
|
||||
self.last_sl_dir = 0
|
||||
return False
|
||||
|
||||
reset_band = max(self.cfg.reset_band_floor, self.cfg.reset_band_k * entry_dev)
|
||||
if abs(dev) <= reset_band:
|
||||
self.last_sl_dir = 0
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _post_sl_dynamic_mult(self) -> float:
|
||||
"""计算止损后SL放宽倍数"""
|
||||
if self.post_sl_dir == 0:
|
||||
return 1.0
|
||||
|
||||
if (time.time() - self.post_sl_ts) > self.cfg.post_sl_sl_max_sec:
|
||||
self.post_sl_dir = 0
|
||||
self.post_sl_vol_scale = 1.0
|
||||
return 1.0
|
||||
|
||||
raw = 1.0 + self.cfg.post_sl_vol_alpha * (self.post_sl_vol_scale - 1.0)
|
||||
raw = max(1.0, raw) # 不缩小止损,只放宽
|
||||
return max(self.cfg.post_sl_mult_min, min(self.cfg.post_sl_mult_max, raw))
|
||||
|
||||
# ----------------- 交易逻辑 -----------------
|
||||
def in_cooldown(self) -> bool:
|
||||
"""检查是否在冷却期内(新增:止损后可额外延长冷却,用于抑制反手连环)"""
|
||||
cd = self.cfg.cooldown_sec_after_exit
|
||||
if self.last_sl_ts and (time.time() - self.last_sl_ts) < self.cfg.reentry_penalty_max_sec:
|
||||
cd += max(0, self.cfg.cooldown_sec_after_sl_extra)
|
||||
return (time.time() - self.last_exit_ts) < cd
|
||||
|
||||
def _adx_blocks_entry(self, adx_val: float, plus_di: float, minus_di: float, want_dir: int) -> bool:
|
||||
"""
|
||||
ADX 趋势过滤:
|
||||
- want_dir: 1=想开多, -1=想开空
|
||||
"""
|
||||
if not self.cfg.enable_adx_filter:
|
||||
return False
|
||||
if adx_val < self.cfg.adx_threshold:
|
||||
return False
|
||||
|
||||
if self.cfg.adx_mode == "block_all":
|
||||
return True
|
||||
|
||||
# block_countertrend:只禁止逆 DI 方向
|
||||
# 上升趋势:+DI > -DI => 禁止开空
|
||||
# 下降趋势:-DI > +DI => 禁止开多
|
||||
if plus_di > minus_di and want_dir == -1:
|
||||
return True
|
||||
if minus_di > plus_di and want_dir == 1:
|
||||
return True
|
||||
return False
|
||||
|
||||
def maybe_enter(self, price: float, ema_value: float, entry_dev: float,
|
||||
adx_val: float, plus_di: float, minus_di: float):
|
||||
"""检查并执行入场(新增:ADX趋势过滤,防止趋势期逆势反复开仓)"""
|
||||
if self.pos != 0:
|
||||
return
|
||||
if self.in_cooldown():
|
||||
return
|
||||
|
||||
dev = (price - ema_value) / ema_value if ema_value else 0.0
|
||||
size = self.calculate_size(price)
|
||||
if size <= 0:
|
||||
return
|
||||
|
||||
penalty_active = self._reentry_penalty_active(dev, entry_dev)
|
||||
|
||||
long_th = -entry_dev
|
||||
short_th = entry_dev
|
||||
|
||||
if penalty_active:
|
||||
if self.last_sl_dir == 1:
|
||||
long_th = -entry_dev * self.cfg.reentry_penalty_mult
|
||||
logger.info(f"多头止损后惩罚生效: long_th={long_th * 100:.3f}%")
|
||||
elif self.last_sl_dir == -1:
|
||||
short_th = entry_dev * self.cfg.reentry_penalty_mult
|
||||
logger.info(f"空头止损后惩罚生效: short_th={short_th * 100:.3f}%")
|
||||
|
||||
logger.info(
|
||||
f"入场检查: price={price:.2f}, ema={ema_value:.2f}, dev={dev * 100:.3f}% "
|
||||
f"(entry_dev={entry_dev * 100:.3f}%, long_th={long_th * 100:.3f}%, short_th={short_th * 100:.3f}%) "
|
||||
f"ADX={adx_val:.2f} +DI={plus_di:.2f} -DI={minus_di:.2f} "
|
||||
f"size={size}, penalty={penalty_active}, last_sl_dir={self.last_sl_dir}"
|
||||
)
|
||||
|
||||
# 先判断信号,再用 ADX 过滤(这样日志更直观)
|
||||
if dev <= long_th:
|
||||
if self._adx_blocks_entry(adx_val, plus_di, minus_di, want_dir=1):
|
||||
logger.warning(
|
||||
f"ADX过滤:趋势期禁止逆势开多(ADX={adx_val:.2f}, +DI={plus_di:.2f}, -DI={minus_di:.2f})"
|
||||
)
|
||||
return
|
||||
|
||||
if self.place_market_order(1, size):
|
||||
self.pos = 1
|
||||
self.entry_price = price
|
||||
self.entry_ts = time.time()
|
||||
self.ding(f"✅开多:dev={dev * 100:.3f}% size={size} entry={price:.2f}")
|
||||
|
||||
elif dev >= short_th:
|
||||
if self._adx_blocks_entry(adx_val, plus_di, minus_di, want_dir=-1):
|
||||
logger.warning(
|
||||
f"ADX过滤:趋势期禁止逆势开空(ADX={adx_val:.2f}, +DI={plus_di:.2f}, -DI={minus_di:.2f})"
|
||||
)
|
||||
return
|
||||
|
||||
if self.place_market_order(4, size):
|
||||
self.pos = -1
|
||||
self.entry_price = price
|
||||
self.entry_ts = time.time()
|
||||
self.ding(f"✅开空:dev={dev * 100:.3f}% size={size} entry={price:.2f}")
|
||||
|
||||
def maybe_exit(self, price: float, tp: float, sl: float, vol_scale: float):
|
||||
"""检查并执行出场"""
|
||||
if self.pos == 0 or self.entry_price is None or self.entry_ts is None:
|
||||
return
|
||||
|
||||
hold = time.time() - self.entry_ts
|
||||
|
||||
if self.pos == 1:
|
||||
pnl = (price - self.entry_price) / self.entry_price
|
||||
else:
|
||||
pnl = (self.entry_price - price) / self.entry_price
|
||||
|
||||
sl_mult = 1.0
|
||||
if self.post_sl_dir == self.pos and self.post_sl_dir != 0:
|
||||
sl_mult = self._post_sl_dynamic_mult()
|
||||
effective_sl = sl * sl_mult
|
||||
|
||||
if pnl >= tp:
|
||||
self.close_position_all()
|
||||
self.ding(f"🎯止盈:pnl={pnl * 100:.3f}% price={price:.2f} tp={tp * 100:.3f}%")
|
||||
self.entry_price, self.entry_ts = None, None
|
||||
self.last_exit_ts = time.time()
|
||||
|
||||
elif pnl <= -effective_sl:
|
||||
sl_dir = self.pos
|
||||
self.close_position_all()
|
||||
self.ding(
|
||||
f"🛑止损:pnl={pnl * 100:.3f}% price={price:.2f} "
|
||||
f"sl={sl * 100:.3f}% effective_sl={effective_sl * 100:.3f}%(×{sl_mult:.2f})",
|
||||
error=True
|
||||
)
|
||||
|
||||
# 记录止损方向与时间
|
||||
self.last_sl_dir = sl_dir
|
||||
self.last_sl_ts = time.time()
|
||||
|
||||
self.post_sl_dir = sl_dir
|
||||
self.post_sl_ts = time.time()
|
||||
self.post_sl_vol_scale = float(vol_scale)
|
||||
|
||||
self.entry_price, self.entry_ts = None, None
|
||||
self.last_exit_ts = time.time()
|
||||
|
||||
elif hold >= self.cfg.max_hold_sec:
|
||||
self.close_position_all()
|
||||
self.ding(f"⏱超时:hold={int(hold)}s pnl={pnl * 100:.3f}% price={price:.2f}")
|
||||
self.entry_price, self.entry_ts = None, None
|
||||
self.last_exit_ts = time.time()
|
||||
|
||||
def notify_status_throttled(self, price: float, ema_value: float, dev: float, bal: float,
|
||||
atr_ratio: float, base_ratio: float, vol_scale: float,
|
||||
entry_dev: float, tp: float, sl: float,
|
||||
entry_floor: float, tp_floor: float, sl_floor: float,
|
||||
adx_val: float, plus_di: float, minus_di: float):
|
||||
"""限频状态通知"""
|
||||
now = time.time()
|
||||
if (now - self._last_status_notify_ts) < self.cfg.status_notify_sec:
|
||||
return
|
||||
self._last_status_notify_ts = now
|
||||
|
||||
direction_str = "多" if self.pos == 1 else ("空" if self.pos == -1 else "无")
|
||||
penalty_active = self._reentry_penalty_active(dev, entry_dev)
|
||||
|
||||
sl_mult = 1.0
|
||||
if self.pos != 0 and self.post_sl_dir == self.pos:
|
||||
sl_mult = self._post_sl_dynamic_mult()
|
||||
|
||||
base_age = int(now - self._base_ratio_ts) if self._base_ratio_ts else -1
|
||||
|
||||
msg = (
|
||||
f"【BitMart {self.cfg.contract_symbol}|1m均值回归(动态阈值+ADX过滤)】\n"
|
||||
f"📊 状态:{direction_str}\n"
|
||||
f"💰 现价:{price:.2f} | EMA{self.cfg.ema_len}:{ema_value:.2f}\n"
|
||||
f"📈 偏离:{dev * 100:.3f}% (入场阈值:±{entry_dev * 100:.3f}%)\n"
|
||||
f"🌊 波动率:ATR比={atr_ratio * 100:.3f}% | 基准={base_ratio * 100:.3f}% | 缩放={vol_scale:.2f}\n"
|
||||
f"🧭 趋势:ADX={adx_val:.2f} | +DI={plus_di:.2f} | -DI={minus_di:.2f} "
|
||||
f"(阈值={self.cfg.adx_threshold:.1f}, 模式={self.cfg.adx_mode})\n"
|
||||
f"🎯 动态Floor:入场={entry_floor * 100:.3f}% | 止盈={tp_floor * 100:.3f}% | 止损={sl_floor * 100:.3f}%\n"
|
||||
f"💰 止盈/止损:{tp * 100:.3f}% / {sl * 100:.3f}% (盈亏比:{tp / sl:.2f})\n"
|
||||
f"🔄 基准刷新:{self.cfg.base_ratio_refresh_sec}s (已过={base_age}s)\n"
|
||||
f"⚠️ 止损同向加门槛:{'开启' if penalty_active else '关闭'} (方向={self.last_sl_dir})\n"
|
||||
f"💳 可用余额:{bal:.2f} USDT | 杠杆:{self.cfg.leverage}x\n"
|
||||
f"⏱️ 持仓限制:{self.cfg.max_hold_sec}s | 冷却:{self.cfg.cooldown_sec_after_exit}s"
|
||||
)
|
||||
self.ding(msg)
|
||||
|
||||
def action(self):
|
||||
"""主循环"""
|
||||
if not self.set_leverage():
|
||||
self.ding("杠杆设置失败,停止运行", error=True)
|
||||
return
|
||||
|
||||
while True:
|
||||
now_dt = datetime.datetime.now()
|
||||
self.pbar.n = now_dt.second
|
||||
self.pbar.refresh()
|
||||
|
||||
# 1. 获取K线数据
|
||||
klines = self.get_klines_cached()
|
||||
if not klines or len(klines) < (max(self.cfg.ema_len + 5, 2 * self.cfg.adx_len + 5)):
|
||||
logger.warning("K线数据不足,等待...")
|
||||
time.sleep(1)
|
||||
continue
|
||||
|
||||
# 2. 计算技术指标
|
||||
last_k = klines[-1]
|
||||
closes = [k["close"] for k in klines[-(self.cfg.ema_len + 1):]]
|
||||
ema_value = self.ema(closes, self.cfg.ema_len)
|
||||
|
||||
price = self.get_last_price(fallback_close=float(last_k["close"]))
|
||||
dev = (price - ema_value) / ema_value if ema_value else 0.0
|
||||
|
||||
# 3. 计算波动率相关指标
|
||||
a = self.atr(klines, self.cfg.atr_len)
|
||||
atr_ratio = (a / price) if price > 0 else 0.0
|
||||
base_ratio = self.get_base_ratio_cached(klines)
|
||||
|
||||
# 4. 计算动态阈值
|
||||
entry_dev, tp, sl, vol_scale, entry_floor, tp_floor, sl_floor = self.dynamic_thresholds(
|
||||
atr_ratio, base_ratio
|
||||
)
|
||||
|
||||
# 5. 计算 ADX(新增)
|
||||
adx_val, plus_di, minus_di = self.adx(klines, self.cfg.adx_len)
|
||||
logger.info(f"ADX: {adx_val:.2f} (+DI={plus_di:.2f}, -DI={minus_di:.2f})")
|
||||
|
||||
# 6. 风控检查
|
||||
self.risk_kill_switch()
|
||||
|
||||
# 7. 获取持仓状态
|
||||
if not self.get_position_status():
|
||||
time.sleep(1)
|
||||
continue
|
||||
|
||||
# 8. 检查交易是否启用
|
||||
if not self.trading_enabled:
|
||||
if self.pos != 0:
|
||||
self.close_position_all()
|
||||
logger.warning("交易被禁用(风控触发),等待...")
|
||||
time.sleep(5)
|
||||
continue
|
||||
|
||||
# 9. 检查危险市场
|
||||
if self.is_danger_market(klines, price):
|
||||
logger.warning("危险模式:高波动/大实体K,暂停开仓")
|
||||
self.maybe_exit(price, tp, sl, vol_scale)
|
||||
time.sleep(self.cfg.tick_refresh_sec)
|
||||
continue
|
||||
|
||||
# 10. 执行交易逻辑
|
||||
self.maybe_exit(price, tp, sl, vol_scale)
|
||||
self.maybe_enter(price, ema_value, entry_dev, adx_val, plus_di, minus_di)
|
||||
|
||||
# 11. 状态通知
|
||||
bal = self.get_assets_available()
|
||||
self.notify_status_throttled(
|
||||
price, ema_value, dev, bal,
|
||||
atr_ratio, base_ratio, vol_scale,
|
||||
entry_dev, tp, sl,
|
||||
entry_floor, tp_floor, sl_floor,
|
||||
adx_val, plus_di, minus_di
|
||||
)
|
||||
|
||||
time.sleep(self.cfg.tick_refresh_sec)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
"""
|
||||
Windows PowerShell:
|
||||
setx BITMART_API_KEY "你的key"
|
||||
setx BITMART_SECRET_KEY "你的secret"
|
||||
setx BITMART_MEMO "合约交易"
|
||||
重新打开终端再运行。
|
||||
|
||||
Linux/macOS:
|
||||
export BITMART_API_KEY="你的key"
|
||||
export BITMART_SECRET_KEY="你的secret"
|
||||
export BITMART_MEMO "合约交易"
|
||||
"""
|
||||
cfg = StrategyConfig()
|
||||
bot = BitmartFuturesMeanReversionBot(cfg)
|
||||
|
||||
logger.remove()
|
||||
logger.add(lambda msg: tqdm.write(msg, end=""), level="INFO")
|
||||
|
||||
try:
|
||||
bot.action()
|
||||
except KeyboardInterrupt:
|
||||
logger.info("程序被用户中断")
|
||||
bot.ding("🤖 策略已手动停止")
|
||||
except Exception as e:
|
||||
logger.error(f"程序异常退出: {e}")
|
||||
bot.ding(f"❌ 策略异常退出: {e}", error=True)
|
||||
raise
|
||||
212
weex/1.html
212
weex/1.html
@@ -1,212 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Stock Details</title>
|
||||
<style>
|
||||
:root {
|
||||
--gap-size: 32px;
|
||||
box-sizing: border-box;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Trebuchet MS', Roboto,
|
||||
Ubuntu, sans-serif;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
header,
|
||||
footer {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
header {
|
||||
justify-content: space-between;
|
||||
padding: 0 var(--gap-size);
|
||||
gap: calc(var(--gap-size) * 2);
|
||||
box-shadow: rgba(0, 0, 0, 0.05) 0 2px 6px 0;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
header #site-logo {
|
||||
font-weight: 600;
|
||||
font-size: 32px;
|
||||
padding: 16px;
|
||||
background: var(
|
||||
--18-promo-gradient-02,
|
||||
linear-gradient(90deg, #00bce5 0%, #2962ff 100%)
|
||||
);
|
||||
-webkit-text-fill-color: transparent;
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
header input[type='search'] {
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
max-width: 400px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
footer {
|
||||
flex-direction: column;
|
||||
padding: calc(var(--gap-size) * 0.5) var(--gap-size);
|
||||
margin-top: var(--gap-size);
|
||||
border-top: solid 2px rgba(0, 0, 0, 0.1);
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
footer p,
|
||||
#powered-by-tv p {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
main {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
padding: 0 calc(var(--gap-size) * 0.5);
|
||||
max-width: 960px;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
grid-gap: var(--gap-size);
|
||||
}
|
||||
|
||||
.span-full-grid,
|
||||
#symbol-info,
|
||||
#advanced-chart,
|
||||
#company-profile,
|
||||
#fundamental-data {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
.span-one-column,
|
||||
#technical-analysis,
|
||||
#top-stories,
|
||||
#powered-by-tv {
|
||||
grid-column: span 1;
|
||||
}
|
||||
|
||||
.skeleton,
|
||||
#ticker-tape,
|
||||
#symbol-info,
|
||||
#advanced-chart,
|
||||
#company-profile,
|
||||
#fundamental-data,
|
||||
#technical-analysis,
|
||||
#top-stories,
|
||||
#ticker-tape {
|
||||
text-align: center;
|
||||
padding: 16px;
|
||||
font-size: 24px;
|
||||
background: rgba(0, 0, 0, 0.075);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#ticker-tape {
|
||||
width: 100%;
|
||||
margin-bottom: var(--gap-size);
|
||||
height: 75px;
|
||||
}
|
||||
|
||||
#symbol-info {
|
||||
height: 175px;
|
||||
}
|
||||
|
||||
#advanced-chart {
|
||||
height: 500px;
|
||||
}
|
||||
|
||||
#company-profile {
|
||||
height:390px;
|
||||
}
|
||||
|
||||
#fundamental-data {
|
||||
height: 490px;
|
||||
}
|
||||
|
||||
#technical-analysis,
|
||||
#top-stories {
|
||||
height: 425px;
|
||||
}
|
||||
|
||||
#powered-by-tv {
|
||||
display: flex;
|
||||
background: #f8f9fd;
|
||||
border: solid 1px #e0e3eb;
|
||||
text-align: justify;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
font-size: 14px;
|
||||
padding: 16px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
#powered-by-tv a, #powered-by-tv a:visited {
|
||||
color: #2962ff;
|
||||
}
|
||||
|
||||
@media (max-width: 800px) {
|
||||
main > section,
|
||||
.span-full-grid,
|
||||
#technical-analysis,
|
||||
#top-stories,
|
||||
#powered-by-tv {
|
||||
grid-column: span 2;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<a id="site-logo" href="#">TradingVista</a>
|
||||
<input type="search" placeholder="Search..." />
|
||||
</header>
|
||||
<nav id="ticker-tape">Ticker Tape</nav>
|
||||
<main>
|
||||
<section id="symbol-info">Symbol Info</section>
|
||||
<section id="advanced-chart">Advanced Chart</section>
|
||||
<section id="company-profile">Company Profile</section>
|
||||
<section id="fundamental-data">Fundamental Data</section>
|
||||
<section id="technical-analysis">Technical Analysis</section>
|
||||
<section id="top-stories">Top Stories</section>
|
||||
<section id="powered-by-tv">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="157" height="21">
|
||||
<use href="/widget-docs/tradingview-logo.svg#tradingview-logo"></use>
|
||||
</svg>
|
||||
<p>
|
||||
Charts and financial information provided by TradingView, a popular
|
||||
charting & trading platform. Check out even more
|
||||
<a href="https://www.tradingview.com/features/">advanced features</a>
|
||||
or <a href="https://www.tradingview.com/widget/">grab charts</a> for
|
||||
your website.
|
||||
</p>
|
||||
</section>
|
||||
</main>
|
||||
<footer>
|
||||
<p>
|
||||
This example page is part of a tutorial for integrating TradingView
|
||||
widgets into your website.
|
||||
</p>
|
||||
<p><a href="https://www.tradingview.com/widget-docs/tutorials/build-page/#build-a-page">View the tutorial</a></p>
|
||||
</footer>
|
||||
</body>
|
||||
<script></script>
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
BIN
weex/database.db
BIN
weex/database.db
Binary file not shown.
293
weex/三分之一策略-5分钟交易.py
Normal file
293
weex/三分之一策略-5分钟交易.py
Normal file
@@ -0,0 +1,293 @@
|
||||
"""
|
||||
WEEX 三分之一策略 — 5分钟K线交易
|
||||
基于 weex/框架.py,策略逻辑与 bitmart/三分之一策略-5分钟交易.py 一致。
|
||||
|
||||
策略规则:
|
||||
1. 触发价(前一根有效K线实体>=0.1):做多=收盘+实体/3,做空=收盘-实体/3
|
||||
2. 信号:当前5分钟K线最高>=做多触发→多;最低<=做空触发→空;同根都触发用开盘价距离判断先后
|
||||
3. 反手一:持空且当前涨到上根最高且上根上影线>0.01%→反手多;持多且当前跌到上根最低且上根下影线>0.01%→反手空
|
||||
4. 反手二:持多且上根上影线>0.01%且当前跌到上根开盘→反手空;持空且上根下影线>0.01%且当前涨到上根开盘→反手多
|
||||
5. 同一根5分钟K线内只交易一次
|
||||
"""
|
||||
import random
|
||||
import sys
|
||||
import time
|
||||
import datetime
|
||||
from pathlib import Path
|
||||
from typing import Optional, Dict, List, Tuple
|
||||
|
||||
from tqdm import tqdm
|
||||
from loguru import logger
|
||||
|
||||
# 保证可导入同目录下的 框架
|
||||
_weex_dir = Path(__file__).resolve().parent
|
||||
if str(_weex_dir) not in sys.path:
|
||||
sys.path.insert(0, str(_weex_dir))
|
||||
from 框架 import Config, WeexFuturesTransaction
|
||||
|
||||
# 5 分钟 K 线类型(若 WEEX 接口名不同可改为实际值)
|
||||
KLINE_TYPE_5M = "MINUTE_5"
|
||||
|
||||
|
||||
class WeexOneThirdStrategy(WeexFuturesTransaction):
|
||||
"""WEEX 三分之一策略(5分钟K线),继承 weex/框架.py"""
|
||||
|
||||
def __init__(self, tge_id):
|
||||
super().__init__(tge_id)
|
||||
self.pbar = tqdm(total=5, desc="等待5分钟K线", ncols=80)
|
||||
self.min_body_size = 0.1
|
||||
self.min_shadow_pct = 0.01
|
||||
self.check_interval = 5
|
||||
self.position_ratio = 100 # 开仓金额 = 余额 / position_ratio
|
||||
self.last_trigger_kline_id: Optional[int] = None
|
||||
self.last_trigger_direction: Optional[str] = None
|
||||
self.last_trade_kline_id: Optional[int] = None
|
||||
|
||||
# ------------------------- 5分钟K线 -------------------------
|
||||
def get_klines_5m(self) -> Optional[List[Dict]]:
|
||||
"""获取 5 分钟 K 线"""
|
||||
return self.get_klines(kline_type=KLINE_TYPE_5M, limit=100)
|
||||
|
||||
# ------------------------- 三分之一策略核心 -------------------------
|
||||
def get_body_size(self, candle: Dict) -> float:
|
||||
return abs(float(candle["open"]) - float(candle["close"]))
|
||||
|
||||
def find_valid_prev_bar(self, all_data: List[Dict], current_idx: int) -> Tuple[Optional[int], Optional[Dict]]:
|
||||
if current_idx <= 0:
|
||||
return None, None
|
||||
for i in range(current_idx - 1, -1, -1):
|
||||
prev = all_data[i]
|
||||
if self.get_body_size(prev) >= self.min_body_size:
|
||||
return i, prev
|
||||
return None, None
|
||||
|
||||
def get_one_third_levels(self, prev: Dict) -> Tuple[Optional[float], Optional[float]]:
|
||||
p_open, p_close = float(prev["open"]), float(prev["close"])
|
||||
body = abs(p_open - p_close)
|
||||
if body < 0.001:
|
||||
return None, None
|
||||
return p_close + body / 3, p_close - body / 3
|
||||
|
||||
def get_upper_shadow(self, candle: Dict) -> float:
|
||||
o, c, h = float(candle["open"]), float(candle["close"]), float(candle["high"])
|
||||
return h - max(o, c)
|
||||
|
||||
def get_lower_shadow(self, candle: Dict) -> float:
|
||||
o, c, l = float(candle["open"]), float(candle["close"]), float(candle["low"])
|
||||
return min(o, c) - l
|
||||
|
||||
def upper_shadow_pct(self, candle: Dict) -> float:
|
||||
o = float(candle["open"])
|
||||
return (self.get_upper_shadow(candle) / o * 100) if o > 0 else 0.0
|
||||
|
||||
def lower_shadow_pct(self, candle: Dict) -> float:
|
||||
o = float(candle["open"])
|
||||
return (self.get_lower_shadow(candle) / o * 100) if o > 0 else 0.0
|
||||
|
||||
def check_reverse_by_prev_high_low(self, kline_data: List[Dict]) -> Tuple[Optional[str], Optional[Dict]]:
|
||||
if len(kline_data) < 2:
|
||||
return None, None
|
||||
curr, prev = kline_data[-1], kline_data[-2]
|
||||
curr_high = float(curr["high"])
|
||||
curr_low = float(curr["low"])
|
||||
prev_high = float(prev["high"])
|
||||
prev_low = float(prev["low"])
|
||||
if self.start == -1 and curr_high >= prev_high and self.upper_shadow_pct(prev) > self.min_shadow_pct:
|
||||
return "long", prev
|
||||
if self.start == 1 and curr_low <= prev_low and self.lower_shadow_pct(prev) > self.min_shadow_pct:
|
||||
return "short", prev
|
||||
return None, None
|
||||
|
||||
def check_reverse_by_prev_open(self, kline_data: List[Dict]) -> Tuple[Optional[str], Optional[Dict]]:
|
||||
if len(kline_data) < 2:
|
||||
return None, None
|
||||
curr, prev = kline_data[-1], kline_data[-2]
|
||||
curr_high = float(curr["high"])
|
||||
curr_low = float(curr["low"])
|
||||
prev_open = float(prev["open"])
|
||||
if self.start == 1 and self.upper_shadow_pct(prev) > self.min_shadow_pct and curr_low <= prev_open:
|
||||
return "short", prev
|
||||
if self.start == -1 and self.lower_shadow_pct(prev) > self.min_shadow_pct and curr_high >= prev_open:
|
||||
return "long", prev
|
||||
return None, None
|
||||
|
||||
def check_realtime_trigger(
|
||||
self, kline_data: List[Dict]
|
||||
) -> Tuple[Optional[str], Optional[float], Optional[Dict], Optional[Dict]]:
|
||||
if len(kline_data) < 2:
|
||||
return None, None, None, None
|
||||
curr = kline_data[-1]
|
||||
curr_kline_id = curr["id"]
|
||||
curr_high = float(curr["high"])
|
||||
curr_low = float(curr["low"])
|
||||
curr_open = float(curr["open"])
|
||||
|
||||
valid_prev_idx, prev = self.find_valid_prev_bar(kline_data, len(kline_data) - 1)
|
||||
if prev is None:
|
||||
return None, None, None, None
|
||||
|
||||
long_trigger, short_trigger = self.get_one_third_levels(prev)
|
||||
if long_trigger is None:
|
||||
return None, None, None, None
|
||||
|
||||
long_triggered = curr_high >= long_trigger
|
||||
short_triggered = curr_low <= short_trigger
|
||||
both_triggered = long_triggered and short_triggered
|
||||
|
||||
direction = None
|
||||
trigger_price = None
|
||||
if both_triggered:
|
||||
dist_to_long = abs(long_trigger - curr_open)
|
||||
dist_to_short = abs(short_trigger - curr_open)
|
||||
if dist_to_short <= dist_to_long:
|
||||
direction, trigger_price = "short", short_trigger
|
||||
else:
|
||||
direction, trigger_price = "long", long_trigger
|
||||
elif short_triggered:
|
||||
direction, trigger_price = "short", short_trigger
|
||||
elif long_triggered:
|
||||
direction, trigger_price = "long", long_trigger
|
||||
|
||||
if direction is None:
|
||||
return None, None, None, None
|
||||
if self.last_trigger_kline_id == curr_kline_id and self.last_trigger_direction == direction:
|
||||
return None, None, None, None
|
||||
return direction, trigger_price, prev, curr
|
||||
|
||||
# ------------------------- 主循环 -------------------------
|
||||
def action(self) -> None:
|
||||
n = 0
|
||||
while True:
|
||||
|
||||
for i in range(3):
|
||||
if self.openBrowser():
|
||||
break
|
||||
else:
|
||||
self.ding("打开 TGE 失败!", error=True)
|
||||
continue
|
||||
|
||||
logger.info("TGE 浏览器已打开")
|
||||
self.close_extra_tabs()
|
||||
self.page.get(url=Config.TRADING_URL)
|
||||
time.sleep(self.check_interval)
|
||||
|
||||
if n == 0 or random.randint(1,11)> 6:
|
||||
n = 1
|
||||
if not self._get_token():
|
||||
self.ding("获取 token 失败", error=True)
|
||||
return
|
||||
|
||||
self.click_safe('x:(//button[normalize-space(text()) = "市价"])')
|
||||
logger.info("WEEX 三分之一策略(5分钟K线)开始监测")
|
||||
|
||||
try:
|
||||
kline_data = self.get_klines_5m()
|
||||
if not kline_data or len(kline_data) < 3:
|
||||
logger.warning("K线数据不足,等待重试...")
|
||||
time.sleep(self.check_interval)
|
||||
continue
|
||||
|
||||
curr = kline_data[-1]
|
||||
curr_kline_id = curr["id"]
|
||||
curr_time_str = datetime.datetime.fromtimestamp(curr["id"] / 1000).strftime("%H:%M:%S")
|
||||
|
||||
if not self.get_position_status():
|
||||
logger.warning("获取仓位失败,使用缓存")
|
||||
|
||||
# 反手一:涨到上根最高/跌到上根最低 + 影线>0.01%
|
||||
rev_dir, rev_prev = self.check_reverse_by_prev_high_low(kline_data)
|
||||
rev_type = "一"
|
||||
if not rev_dir:
|
||||
rev_dir, rev_prev = self.check_reverse_by_prev_open(kline_data)
|
||||
rev_type = "二"
|
||||
|
||||
if rev_dir and self.last_trade_kline_id != curr_kline_id:
|
||||
balance = self.get_available_balance()
|
||||
amount = int((balance or 0) / self.position_ratio)
|
||||
if amount > 0:
|
||||
self.ding(f"反手信号{rev_type} {rev_dir} 金额={amount}")
|
||||
if rev_dir == "long":
|
||||
self.平仓()
|
||||
time.sleep(1)
|
||||
self.开单(marketPriceLongOrder=1, size=amount)
|
||||
else:
|
||||
self.平仓()
|
||||
time.sleep(1)
|
||||
self.开单(marketPriceLongOrder=-1, size=amount)
|
||||
self.last_trade_kline_id = curr_kline_id
|
||||
self.last_trigger_kline_id = curr_kline_id
|
||||
self.last_trigger_direction = rev_dir
|
||||
time.sleep(self.check_interval)
|
||||
continue
|
||||
|
||||
# 主信号:三分之一触发
|
||||
direction, trigger_price, valid_prev, curr_kline = self.check_realtime_trigger(kline_data)
|
||||
|
||||
if direction:
|
||||
if self.last_trade_kline_id == curr_kline_id:
|
||||
self.last_trigger_kline_id = curr_kline_id
|
||||
self.last_trigger_direction = direction
|
||||
time.sleep(self.check_interval)
|
||||
continue
|
||||
|
||||
if (direction == "long" and self.start == 1) or (direction == "short" and self.start == -1):
|
||||
self.last_trigger_kline_id = curr_kline_id
|
||||
self.last_trigger_direction = direction
|
||||
time.sleep(self.check_interval)
|
||||
continue
|
||||
|
||||
balance = self.get_available_balance()
|
||||
amount = int((balance or 0) / self.position_ratio)
|
||||
if amount <= 0:
|
||||
time.sleep(self.check_interval)
|
||||
continue
|
||||
|
||||
executed = False
|
||||
if direction == "long":
|
||||
if self.start == -1:
|
||||
self.ding("平空反手开多")
|
||||
self.平仓()
|
||||
time.sleep(1)
|
||||
self.开单(marketPriceLongOrder=1, size=amount)
|
||||
executed = True
|
||||
elif self.start == 0:
|
||||
self.ding("无仓位开多")
|
||||
self.开单(marketPriceLongOrder=1, size=amount)
|
||||
executed = True
|
||||
elif direction == "short":
|
||||
if self.start == 1:
|
||||
self.ding("平多反手开空")
|
||||
self.平仓()
|
||||
time.sleep(1)
|
||||
self.开单(marketPriceLongOrder=-1, size=amount)
|
||||
executed = True
|
||||
elif self.start == 0:
|
||||
self.ding("无仓位开空")
|
||||
self.开单(marketPriceLongOrder=-1, size=amount)
|
||||
executed = True
|
||||
|
||||
if executed:
|
||||
self.last_trade_kline_id = curr_kline_id
|
||||
self.get_position_status()
|
||||
self.ding(f"三分之一信号 {direction} 触发价={trigger_price:.2f} 金额={amount}")
|
||||
self.last_trigger_kline_id = curr_kline_id
|
||||
self.last_trigger_direction = direction
|
||||
else:
|
||||
logger.debug(f"[{curr_time_str}] O={curr['open']:.2f} H={curr['high']:.2f} L={curr['low']:.2f}")
|
||||
|
||||
time.sleep(self.check_interval)
|
||||
if random.randint(1, 11) > 7:
|
||||
self.page.close()
|
||||
time.sleep(5)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logger.info("用户中断")
|
||||
break
|
||||
except Exception as e:
|
||||
logger.error(f"主循环异常: {e}")
|
||||
self.ding(f"运行异常: {e}", error=True)
|
||||
time.sleep(self.check_interval)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
WeexOneThirdStrategy(tge_id="86837a981aba4576be6916a0ef6ad785").action()
|
||||
398
weex/优化开仓方向版本.py
398
weex/优化开仓方向版本.py
@@ -1,398 +0,0 @@
|
||||
"""
|
||||
量化交易回测系统 - 优化版
|
||||
功能:基于包住形态的交易信号识别和回测分析
|
||||
作者:量化交易团队
|
||||
版本:2.0
|
||||
"""
|
||||
|
||||
import datetime
|
||||
from typing import List, Dict, Tuple, Optional, Any
|
||||
from dataclasses import dataclass
|
||||
from loguru import logger
|
||||
from peewee import fn
|
||||
from models.weex import Weex15, Weex1
|
||||
|
||||
|
||||
# ===============================================================
|
||||
# 📊 配置管理类
|
||||
# ===============================================================
|
||||
|
||||
@dataclass
|
||||
class BacktestConfig:
|
||||
"""回测配置类"""
|
||||
# 交易参数
|
||||
take_profit: float = 8.0 # 止盈点数
|
||||
stop_loss: float = -1.0 # 止损点数
|
||||
contract_size: float = 10000 # 合约规模
|
||||
open_fee: float = 5.0 # 开仓手续费
|
||||
close_fee_rate: float = 0.0005 # 平仓手续费率
|
||||
|
||||
# 回测日期范围
|
||||
start_date: str = "2025-7-1"
|
||||
end_date: str = "2025-7-31"
|
||||
|
||||
# 信号参数
|
||||
enable_bear_bull_engulf: bool = True # 涨包跌信号
|
||||
enable_bull_bear_engulf: bool = True # 跌包涨信号
|
||||
|
||||
def __post_init__(self):
|
||||
"""验证配置参数"""
|
||||
if self.take_profit <= 0:
|
||||
raise ValueError("止盈点数必须大于0")
|
||||
if self.stop_loss >= 0:
|
||||
raise ValueError("止损点数必须小于0")
|
||||
|
||||
|
||||
@dataclass
|
||||
class TradeRecord:
|
||||
"""交易记录类"""
|
||||
entry_time: datetime.datetime
|
||||
exit_time: datetime.datetime
|
||||
signal_type: str
|
||||
direction: str
|
||||
entry_price: float
|
||||
exit_price: float
|
||||
profit_loss: float
|
||||
profit_amount: float
|
||||
total_fee: float
|
||||
net_profit: float
|
||||
|
||||
|
||||
@dataclass
|
||||
class SignalStats:
|
||||
"""信号统计类"""
|
||||
signal_name: str
|
||||
count: int = 0
|
||||
wins: int = 0
|
||||
total_profit: float = 0.0
|
||||
|
||||
@property
|
||||
def win_rate(self) -> float:
|
||||
"""胜率计算"""
|
||||
return (self.wins / self.count * 100) if self.count > 0 else 0.0
|
||||
|
||||
@property
|
||||
def avg_profit(self) -> float:
|
||||
"""平均盈利"""
|
||||
return self.total_profit / self.count if self.count > 0 else 0.0
|
||||
|
||||
|
||||
# ===============================================================
|
||||
# 📊 数据获取模块
|
||||
# ===============================================================
|
||||
|
||||
def get_data_by_date(model, date_str):
|
||||
"""按天获取指定表的数据"""
|
||||
try:
|
||||
target_date = datetime.datetime.strptime(date_str, '%Y-%m-%d')
|
||||
except ValueError:
|
||||
logger.error("日期格式不正确,请使用 YYYY-MM-DD 格式。")
|
||||
return []
|
||||
|
||||
start_ts = int(target_date.timestamp() * 1000)
|
||||
end_ts = int((target_date + datetime.timedelta(days=1)).timestamp() * 1000) - 1
|
||||
|
||||
query = (model
|
||||
.select()
|
||||
.where(model.id.between(start_ts, end_ts))
|
||||
.order_by(model.id.asc()))
|
||||
|
||||
return [
|
||||
{'id': i.id, 'open': i.open, 'high': i.high, 'low': i.low, 'close': i.close}
|
||||
for i in query
|
||||
]
|
||||
|
||||
|
||||
def get_future_data_1min(start_ts, end_ts):
|
||||
"""获取指定时间范围内的 1 分钟数据"""
|
||||
query = (Weex1
|
||||
.select()
|
||||
.where(Weex1.id.between(start_ts, end_ts))
|
||||
.order_by(Weex1.id.asc()))
|
||||
return [{'id': i.id, 'open': i.open, 'high': i.high, 'low': i.low, 'close': i.close} for i in query]
|
||||
|
||||
|
||||
# ===============================================================
|
||||
# 📈 信号判定模块
|
||||
# ===============================================================
|
||||
|
||||
def is_bullish(c): return float(c['open']) < float(c['close'])
|
||||
|
||||
|
||||
def is_bearish(c): return float(c['open']) > float(c['close'])
|
||||
|
||||
|
||||
def check_signal(prev, curr):
|
||||
"""判断是否出现包住形态"""
|
||||
p_open, p_close = float(prev['open']), float(prev['close'])
|
||||
c_open, c_close = float(curr['open']), float(curr['close'])
|
||||
|
||||
# 前跌后涨包住 -> 做多
|
||||
if is_bullish(curr) and is_bearish(prev) and c_open <= p_close and c_close >= p_open:
|
||||
return "long", "bear_bull_engulf"
|
||||
|
||||
# 前涨后跌包住 -> 做空
|
||||
if is_bearish(curr) and is_bullish(prev) and c_open >= p_close and c_close <= p_open:
|
||||
return "short", "bull_bear_engulf"
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
# ===============================================================
|
||||
# 💹 回测模拟模块(使用 1 分钟数据)
|
||||
# ===============================================================
|
||||
|
||||
# ---------- 替换后的 simulate_trade ----------
|
||||
def simulate_trade(direction, entry_price, entry_time, next_15min_time, tp=8, sl=-1):
|
||||
"""
|
||||
返回 (exit_price, profit_diff, exit_time, exit_reason)
|
||||
exit_reason: 'tp' (触及止盈并以tp价平仓),
|
||||
'sl' (触及止损并以sl价平仓),
|
||||
'open_tp' (开盘跳空并以开盘价止盈),
|
||||
'open_sl' (开盘跳空并以开盘价止损),
|
||||
'timeout' (到分析窗口末尾,用最后一根收盘价平仓)
|
||||
注意:sl 参数为负数(你的设定),但在计算止损价时已处理方向
|
||||
"""
|
||||
future_candles = get_future_data_1min(entry_time, next_15min_time)
|
||||
if not future_candles:
|
||||
return None, 0.0, None, None
|
||||
|
||||
# 计算目标价位(数值)
|
||||
tp_price = entry_price + tp if direction == "long" else entry_price - tp
|
||||
sl_price = entry_price + sl if direction == "long" else entry_price - sl # sl 为负数,long 情况为 entry + (负数) -> 小于 entry
|
||||
|
||||
for candle in future_candles:
|
||||
open_p = float(candle['open'])
|
||||
high = float(candle['high'])
|
||||
low = float(candle['low'])
|
||||
ts = candle['id']
|
||||
|
||||
if direction == "long":
|
||||
# 开盘跳空(以开盘价直接触及止盈/止损)
|
||||
if open_p >= tp_price:
|
||||
return open_p, open_p - entry_price, ts, 'open_tp'
|
||||
if open_p <= sl_price:
|
||||
return open_p, open_p - entry_price, ts, 'open_sl'
|
||||
# 盘中触及
|
||||
if high >= tp_price:
|
||||
return tp_price, tp, ts, 'tp'
|
||||
if low <= sl_price:
|
||||
return sl_price, sl, ts, 'sl'
|
||||
else: # short
|
||||
if open_p <= tp_price:
|
||||
return open_p, entry_price - open_p, ts, 'open_tp'
|
||||
if open_p >= sl_price:
|
||||
return open_p, entry_price - open_p, ts, 'open_sl'
|
||||
if low <= tp_price:
|
||||
return tp_price, tp, ts, 'tp'
|
||||
if high >= sl_price:
|
||||
return sl_price, sl, ts, 'sl'
|
||||
|
||||
# 未触发止盈止损,用最后一根收盘价平仓(视为 timeout)
|
||||
final = future_candles[-1]
|
||||
final_price = float(final['close'])
|
||||
diff = (final_price - entry_price) if direction == "long" else (entry_price - final_price)
|
||||
return final_price, diff, final['id'], 'timeout'
|
||||
|
||||
|
||||
# ---------- 替换后的 backtest_single_position ----------
|
||||
def backtest_single_position(dates, tp, sl):
|
||||
"""
|
||||
单笔持仓回测(增强版),加入连续3次止损触发反向开仓(转向单)逻辑
|
||||
- 只有当 exit_reason 属于 'sl' 或 'open_sl' 时才算“真实止损”
|
||||
- 当连续真实止损计数达到 3 时,**下一笔**信号反向开仓(且该转向单不计入连续止损统计)
|
||||
"""
|
||||
all_data = []
|
||||
for date_str in dates:
|
||||
all_data.extend(get_data_by_date(Weex15, date_str))
|
||||
all_data.sort(key=lambda x: x['id'])
|
||||
|
||||
stats = {
|
||||
"bear_bull_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "涨包跌"},
|
||||
"bull_bear_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "跌包涨"},
|
||||
}
|
||||
|
||||
trades = []
|
||||
current_position = None # 当前持仓信息 dict or None
|
||||
|
||||
consec_sl_count = 0 # 连续真实止损计数(只有 exit_reason 为 'sl' 或 'open_sl' 时 +1)
|
||||
reverse_next_signal = False # 是否需要将下一笔信号取反(由连续3次止损触发)
|
||||
ignore_next_result = False # 标记下一笔成交的结果是否要被忽略(用于转向单:转向单不计入连续止损计数,也不影响计数)
|
||||
|
||||
for idx in range(1, len(all_data) - 1):
|
||||
prev, curr = all_data[idx - 1], all_data[idx]
|
||||
entry_candle = all_data[idx + 1]
|
||||
|
||||
# 原始信号判定
|
||||
direction, signal = check_signal(prev, curr)
|
||||
if not direction:
|
||||
continue
|
||||
|
||||
# 如果需要反向下一笔信号(由之前三次止损触发),则翻转 direction 并标记为转向单(只对这一次有效)
|
||||
is_reversal_trade = False
|
||||
if reverse_next_signal:
|
||||
direction = 'long' if direction == 'short' else 'short'
|
||||
is_reversal_trade = True
|
||||
reverse_next_signal = False
|
||||
ignore_next_result = True # 这笔成交的平仓结果不影响 consec_sl_count
|
||||
|
||||
# 下一个 15 分钟K线的时间范围(用 idx+50 作为 15min 窗口近似)
|
||||
next_15min_time = all_data[idx + 50]['id'] if idx + 50 < len(all_data) else all_data[-1]['id']
|
||||
entry_price = float(entry_candle['open'])
|
||||
|
||||
# 如果已有持仓
|
||||
if current_position:
|
||||
# 同向信号 -> 跳过(维持现有持仓)
|
||||
if current_position['direction'] == direction:
|
||||
continue
|
||||
# 反向信号 -> 先按当前位置止盈止损平仓,再根据规则决定是否开新仓
|
||||
else:
|
||||
exit_price, diff, exit_time, exit_reason = simulate_trade(
|
||||
current_position['direction'],
|
||||
current_position['entry_price'],
|
||||
current_position['entry_time'],
|
||||
entry_candle['id'],
|
||||
tp=tp,
|
||||
sl=sl
|
||||
)
|
||||
if exit_price is not None:
|
||||
trades.append({
|
||||
"entry_time": datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
"exit_time": datetime.datetime.fromtimestamp(exit_time / 1000),
|
||||
"signal": current_position['signal'],
|
||||
"direction": "做多" if current_position['direction'] == "long" else "做空",
|
||||
"entry": current_position['entry_price'],
|
||||
"exit": exit_price,
|
||||
"diff": diff,
|
||||
"exit_reason": exit_reason,
|
||||
"is_reversal_trade": current_position.get('is_reversal_trade', False)
|
||||
})
|
||||
# 更新统计(只有非转向单会计入统计?按你原逻辑计入)
|
||||
stats_key = 'bear_bull_engulf' if current_position['signal'] == '涨包跌' else 'bull_bear_engulf'
|
||||
stats[stats_key]['count'] += 1
|
||||
stats[stats_key]['total_profit'] += diff
|
||||
if diff > 0:
|
||||
stats[stats_key]['wins'] += 1
|
||||
|
||||
# 根据 exit_reason 更新 consec_sl_count(但如果当时该仓为被标记为 ignore_result,则不变)
|
||||
if not current_position.get('ignore_result', False):
|
||||
if exit_reason in ('sl', 'open_sl'):
|
||||
consec_sl_count += 1
|
||||
else:
|
||||
consec_sl_count = 0
|
||||
|
||||
# 如果累计达到 3 次真实止损 -> 标记下一笔反向
|
||||
if consec_sl_count >= 3:
|
||||
reverse_next_signal = True
|
||||
consec_sl_count = 0 # 触发后清零(下一笔为转向单)
|
||||
else:
|
||||
# 如果这笔被标记为 ignore(通常是前面是转向单),则不影响计数
|
||||
pass
|
||||
|
||||
current_position = None # 清空持仓
|
||||
|
||||
# 开新仓(注意:如果这笔是转向单,我们之前已经取反了 direction,并设置了 is_reversal_trade 和 ignore_next_result)
|
||||
current_position = {
|
||||
"direction": direction,
|
||||
"signal": stats[signal]['name'],
|
||||
"entry_price": entry_price,
|
||||
"entry_time": entry_candle['id'],
|
||||
"is_reversal_trade": is_reversal_trade,
|
||||
"ignore_result": ignore_next_result # 如果为 True,则本仓平仓结果不计入 consec_sl_count
|
||||
}
|
||||
|
||||
# 如果我们标记了 ignore_next_result(说明这是转向单),只在刚开仓后清除标记,确保仅忽略这笔的**平仓**结果
|
||||
if ignore_next_result:
|
||||
# 清掉,只有这笔仓的平仓结果需要被忽略(记录在 current_position 中),
|
||||
# 后续在处理平仓时会读取 current_position['ignore_result'] 并决定是否影响计数
|
||||
ignore_next_result = False
|
||||
|
||||
# 最后一笔持仓如果未平仓,用最后收盘价平掉
|
||||
if current_position:
|
||||
exit_price, diff, exit_time, exit_reason = simulate_trade(
|
||||
current_position['direction'],
|
||||
current_position['entry_price'],
|
||||
current_position['entry_time'],
|
||||
all_data[-1]['id'],
|
||||
tp=tp,
|
||||
sl=sl
|
||||
)
|
||||
if exit_price is not None:
|
||||
trades.append({
|
||||
"entry_time": datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
"exit_time": datetime.datetime.fromtimestamp(exit_time / 1000),
|
||||
"signal": current_position['signal'],
|
||||
"direction": "做多" if current_position['direction'] == "long" else "做空",
|
||||
"entry": current_position['entry_price'],
|
||||
"exit": exit_price,
|
||||
"diff": diff,
|
||||
"exit_reason": exit_reason,
|
||||
"is_reversal_trade": current_position.get('is_reversal_trade', False)
|
||||
})
|
||||
stats_key = 'bear_bull_engulf' if current_position['signal'] == '涨包跌' else 'bull_bear_engulf'
|
||||
stats[stats_key]['count'] += 1
|
||||
stats[stats_key]['total_profit'] += diff
|
||||
if diff > 0:
|
||||
stats[stats_key]['wins'] += 1
|
||||
|
||||
# 最后一笔是否计入连续止损计数
|
||||
if not current_position.get('ignore_result', False):
|
||||
if exit_reason in ('sl', 'open_sl'):
|
||||
consec_sl_count += 1
|
||||
else:
|
||||
consec_sl_count = 0
|
||||
if consec_sl_count >= 3:
|
||||
# 可选择记录或告警,这里仅重置计数
|
||||
consec_sl_count = 0
|
||||
|
||||
return trades, stats
|
||||
|
||||
|
||||
|
||||
# ===============================================================
|
||||
# 🚀 启动主流程
|
||||
# ===============================================================
|
||||
|
||||
if __name__ == '__main__':
|
||||
dates = [f"2025-9-{i}" for i in range(1, 31)]
|
||||
|
||||
trades, stats = backtest_single_position(dates, tp=50, sl=-10)
|
||||
|
||||
logger.info("===== 每笔交易详情 =====")
|
||||
for t in trades:
|
||||
logger.info(
|
||||
f"{t['entry_time']} {t['direction']}({t['signal']}) "
|
||||
f"入场={t['entry']:.2f} 出场={t['exit']:.2f} 出场时间={t['exit_time']} "
|
||||
f"差价={t['diff']:.2f}"
|
||||
)
|
||||
|
||||
total_profit = sum(t['diff'] / t['entry'] * 10000 for t in trades)
|
||||
total_fee = sum(5 + 10000 / t['entry'] * t['exit'] * 0.0005 for t in trades)
|
||||
|
||||
print(f"\n一共交易笔数:{len(trades)}")
|
||||
print(f"一共盈利:{total_profit:.2f}")
|
||||
print(f"一共手续费:{total_fee:.2f}")
|
||||
print(f"净利润:{total_profit - total_fee:.2f}")
|
||||
print("\n===== 信号统计 =====")
|
||||
|
||||
# ===============================================================================================================================
|
||||
|
||||
# for i in range(1, 16):
|
||||
# for i1 in range(1, 51):
|
||||
# trades, stats = backtest_single_position(dates, tp=i1, sl=-i)
|
||||
#
|
||||
# total_profit = sum(t['diff'] / t['entry'] * 10000 for t in trades)
|
||||
# total_fee = sum(5 + 10000 / t['entry'] * t['exit'] * 0.0005 for t in trades)
|
||||
#
|
||||
# if total_profit > total_fee * 0.1:
|
||||
# print("\n===== 信号统计 =====")
|
||||
# print(f"止盈:{i1}, 止损:{i}")
|
||||
# print(f"\n一共交易笔数:{len(trades)}")
|
||||
# print(f"一共盈利:{total_profit:.2f}")
|
||||
# print(f"一共手续费:{total_fee:.2f}")
|
||||
# print(f"净利润:{total_profit - total_fee * 0.1}")
|
||||
|
||||
# 需要优化,目前有两种情况,第一种,同向,不如说上一单开单是涨,上一单还没有结束,当前信号来了,就不开单,等上一单到了上一单的止损位或者止盈位在平仓
|
||||
# 第二种,方向,上一单是涨,上一单还没有结束,当前信号来了,是跌,然后就按照现在这个信号要开仓的位置,平掉上一单,然后开一单方向的,
|
||||
# 一笔中可能有好几次信号,都按照上面的规则去判断,要保证同一时间,只会有一笔持仓,
|
||||
# 打印每笔的交易详细,如果一笔中同向,输入为一条交易记录
|
||||
789
weex/分析开单数据.py
789
weex/分析开单数据.py
@@ -1,789 +0,0 @@
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
log_data = """
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 02:00:00号 做空(跌包涨) 入场=4474.69 出场=4476.69 出场时间=1756664100000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 03:15:00号 做空(跌包涨) 入场=4472.09 出场=4474.09 出场时间=1756668600000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 03:45:00号 做多(涨包跌) 入场=4474.15 出场=4472.15 出场时间=1756670400000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 04:00:00号 做空(跌包涨) 入场=4453.24 出场=4455.24 出场时间=1756671300000 差价=-2.00 盈利=-4.49 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 05:45:00号 做多(涨包跌) 入场=4473.88 出场=4471.88 出场时间=1756677600000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 06:45:00号 做空(跌包涨) 入场=4439.92 出场=4441.92 出场时间=1756681200000 差价=-2.00 盈利=-4.50 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 07:15:00号 做空(跌包涨) 入场=4432.11 出场=4402.11 出场时间=1756683000000 差价=30.00 盈利=67.69 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 08:15:00号 做空(跌包涨) 入场=4384.50 出场=4386.50 出场时间=1756686600000 差价=-2.00 盈利=-4.56 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 08:45:00号 做多(涨包跌) 入场=4393.52 出场=4391.52 出场时间=1756688400000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 09:15:00号 做空(跌包涨) 入场=4391.40 出场=4393.40 出场时间=1756690200000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 09:30:00号 做多(涨包跌) 入场=4405.79 出场=4403.79 出场时间=1756692000000 差价=-2.00 盈利=-4.54 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 12:30:00号 做空(跌包涨) 入场=4386.76 出场=4388.76 出场时间=1756702800000 差价=-2.00 盈利=-4.56 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 13:15:00号 做空(跌包涨) 入场=4372.37 出场=4374.37 出场时间=1756704600000 差价=-2.00 盈利=-4.57 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 13:30:00号 做多(涨包跌) 入场=4390.78 出场=4388.78 出场时间=1756706400000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 14:30:00号 做多(涨包跌) 入场=4394.00 出场=4392.00 出场时间=1756710000000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-01 15:30:00号 做多(涨包跌) 入场=4419.10 出场=4449.10 出场时间=1756712700000 差价=30.00 盈利=67.89 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-01 18:30:00号 做空(跌包涨) 入场=4429.31 出场=4399.31 出场时间=1756723500000 差价=30.00 盈利=67.73 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-01 19:15:00号 做空(跌包涨) 入场=4399.49 出场=4401.49 出场时间=1756728000000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-01 20:45:00号 做空(跌包涨) 入场=4395.36 出场=4397.36 出场时间=1756731600000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-01 21:45:00号 做空(跌包涨) 入场=4384.90 出场=4386.90 出场时间=1756735200000 差价=-2.00 盈利=-4.56 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-01 22:15:00号 做多(涨包跌) 入场=4396.00 出场=4394.00 出场时间=1756737000000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-01 22:45:00号 做空(跌包涨) 入场=4389.51 出场=4391.51 出场时间=1756738800000 差价=-2.00 盈利=-4.56 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-01 23:15:00号 做空(跌包涨) 入场=4381.89 出场=4351.89 出场时间=1756741500000 差价=30.00 盈利=68.46 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-02 00:30:00号 做空(跌包涨) 入场=4358.69 出场=4360.69 出场时间=1756745100000 差价=-2.00 盈利=-4.59 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-02 01:00:00号 做空(跌包涨) 入场=4353.30 出场=4323.30 出场时间=1756746900000 差价=30.00 盈利=68.91 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-02 02:15:00号 做多(涨包跌) 入场=4337.86 出场=4335.86 出场时间=1756751400000 差价=-2.00 盈利=-4.61 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-02 03:00:00号 做多(涨包跌) 入场=4345.08 出场=4343.08 出场时间=1756757700000 差价=-2.00 盈利=-4.60 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-02 03:45:00号 做多(涨包跌) 入场=4363.92 出场=4361.92 出场时间=1756756800000 差价=-2.00 盈利=-4.58 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-02 04:15:00号 做空(跌包涨) 入场=4358.28 出场=4328.28 出场时间=1756758600000 差价=30.00 盈利=68.83 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-02 07:00:00号 做多(涨包跌) 入场=4282.53 出场=4312.53 出场时间=1756770300000 差价=30.00 盈利=70.05 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-02 08:45:00号 做空(跌包涨) 入场=4291.71 出场=4293.71 出场时间=1756774800000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-02 09:00:00号 做多(涨包跌) 入场=4302.49 出场=4300.49 出场时间=1756775700000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-02 09:30:00号 做多(涨包跌) 入场=4308.80 出场=4338.80 出场时间=1756777500000 差价=30.00 盈利=69.62 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-02 11:00:00号 做多(涨包跌) 入场=4359.11 出场=4389.11 出场时间=1756782900000 差价=30.00 盈利=68.82 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 12:00:00号 做空(跌包涨) 入场=4365.21 出场=4367.21 出场时间=1756786500000 差价=-2.00 盈利=-4.58 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 12:15:00号 做多(涨包跌) 入场=4374.91 出场=4372.91 出场时间=1756789200000 差价=-2.00 盈利=-4.57 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 13:15:00号 做空(跌包涨) 入场=4377.72 出场=4379.72 出场时间=1756791000000 差价=-2.00 盈利=-4.57 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 14:45:00号 做多(涨包跌) 入场=4406.35 出场=4404.35 出场时间=1756796400000 差价=-2.00 盈利=-4.54 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 15:15:00号 做空(跌包涨) 入场=4391.65 出场=4393.65 出场时间=1756798200000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 15:45:00号 做空(跌包涨) 入场=4384.00 出场=4386.00 出场时间=1756800900000 差价=-2.00 盈利=-4.56 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 16:30:00号 做多(涨包跌) 入场=4394.20 出场=4392.20 出场时间=1756802700000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 17:00:00号 做空(跌包涨) 入场=4389.98 出场=4391.98 出场时间=1756804500000 差价=-2.00 盈利=-4.56 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 18:15:00号 做空(跌包涨) 入场=4394.30 出场=4364.30 出场时间=1756812600000 差价=30.00 盈利=68.27 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 19:00:00号 做多(涨包跌) 入场=4389.96 出场=4387.96 出场时间=1756811700000 差价=-2.00 盈利=-4.56 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 19:15:00号 做空(跌包涨) 入场=4375.68 出场=4345.68 出场时间=1756813500000 差价=30.00 盈利=68.56 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 20:00:00号 做空(跌包涨) 入场=4346.32 出场=4316.32 出场时间=1756816200000 差价=30.00 盈利=69.02 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 20:45:00号 做空(跌包涨) 入场=4300.58 出场=4302.58 出场时间=1756818900000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-03 00:30:00号 做空(跌包涨) 入场=4280.64 出场=4282.64 出场时间=1756831500000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.367 | INFO | __main__:<module>:216 - 2025-09-03 01:15:00号 做空(跌包涨) 入场=4289.63 出场=4291.63 出场时间=1756834200000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 01:30:00号 做多(涨包跌) 入场=4307.71 出场=4305.71 出场时间=1756835100000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 02:00:00号 做多(涨包跌) 入场=4317.18 出场=4315.18 出场时间=1756836900000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 02:15:00号 做空(跌包涨) 入场=4293.70 出场=4295.70 出场时间=1756837800000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 03:00:00号 做多(涨包跌) 入场=4294.57 出场=4292.57 出场时间=1756840500000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 03:15:00号 做空(跌包涨) 入场=4271.11 出场=4273.11 出场时间=1756841400000 差价=-2.00 盈利=-4.68 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 03:45:00号 做多(涨包跌) 入场=4275.15 出场=4273.15 出场时间=1756843200000 差价=-2.00 盈利=-4.68 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 04:15:00号 做多(涨包跌) 入场=4294.16 出场=4324.16 出场时间=1756845000000 差价=30.00 盈利=69.86 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 05:15:00号 做多(涨包跌) 入场=4321.16 出场=4319.16 出场时间=1756848600000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 05:30:00号 做空(跌包涨) 入场=4308.71 出场=4310.71 出场时间=1756849500000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 05:45:00号 做多(涨包跌) 入场=4326.63 出场=4324.63 出场时间=1756850400000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 06:15:00号 做空(跌包涨) 入场=4323.50 出场=4325.50 出场时间=1756852200000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 06:45:00号 做多(涨包跌) 入场=4325.63 出场=4323.63 出场时间=1756854000000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 07:30:00号 做空(跌包涨) 入场=4318.13 出场=4320.13 出场时间=1756856700000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 08:00:00号 做多(涨包跌) 入场=4324.58 出场=4322.58 出场时间=1756858500000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 08:30:00号 做多(涨包跌) 入场=4325.28 出场=4323.28 出场时间=1756860300000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 08:45:00号 做空(跌包涨) 入场=4315.84 出场=4285.84 出场时间=1756862100000 差价=30.00 盈利=69.51 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 10:30:00号 做多(涨包跌) 入场=4351.50 出场=4349.50 出场时间=1756867500000 差价=-2.00 盈利=-4.60 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 12:00:00号 做多(涨包跌) 入场=4332.54 出场=4330.54 出场时间=1756872900000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 12:15:00号 做空(跌包涨) 入场=4318.84 出场=4320.84 出场时间=1756873800000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 13:15:00号 做空(跌包涨) 入场=4332.97 出场=4302.97 出场时间=1756879200000 差价=30.00 盈利=69.24 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 14:30:00号 做多(涨包跌) 入场=4314.99 出场=4312.99 出场时间=1756881900000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 15:45:00号 做多(涨包跌) 入场=4309.16 出场=4307.16 出场时间=1756886400000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 16:15:00号 做多(涨包跌) 入场=4321.12 出场=4319.12 出场时间=1756888200000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 16:45:00号 做多(涨包跌) 入场=4323.65 出场=4321.65 出场时间=1756890000000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 17:00:00号 做空(跌包涨) 入场=4316.15 出场=4318.15 出场时间=1756890900000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 17:30:00号 做多(涨包跌) 入场=4318.40 出场=4348.40 出场时间=1756892700000 差价=30.00 盈利=69.47 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 19:00:00号 做多(涨包跌) 入场=4369.61 出场=4367.61 出场时间=1756898100000 差价=-2.00 盈利=-4.58 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 19:45:00号 做多(涨包跌) 入场=4380.09 出场=4378.09 出场时间=1756900800000 差价=-2.00 盈利=-4.57 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 20:00:00号 做空(跌包涨) 入场=4369.58 出场=4371.58 出场时间=1756902600000 差价=-2.00 盈利=-4.58 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 20:45:00号 做空(跌包涨) 入场=4349.99 出场=4351.99 出场时间=1756904400000 差价=-2.00 盈利=-4.60 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 21:45:00号 做多(涨包跌) 入场=4417.77 出场=4447.77 出场时间=1756908000000 差价=30.00 盈利=67.91 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 23:45:00号 做多(涨包跌) 入场=4469.03 出场=4467.03 出场时间=1756915200000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-04 01:00:00号 做多(涨包跌) 入场=4464.29 出场=4462.29 出场时间=1756923300000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-04 02:30:00号 做空(跌包涨) 入场=4467.09 出场=4469.09 出场时间=1756928700000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-04 03:45:00号 做多(涨包跌) 入场=4463.71 出场=4461.71 出场时间=1756931400000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-04 05:15:00号 做多(涨包跌) 入场=4465.31 出场=4463.31 出场时间=1756935000000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-04 05:45:00号 做空(跌包涨) 入场=4454.44 出场=4456.44 出场时间=1756936800000 差价=-2.00 盈利=-4.49 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-04 07:15:00号 做空(跌包涨) 入场=4458.63 出场=4460.63 出场时间=1756942200000 差价=-2.00 盈利=-4.49 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 07:45:00号 做空(跌包涨) 入场=4446.35 出场=4448.35 出场时间=1756944000000 差价=-2.00 盈利=-4.50 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 09:15:00号 做空(跌包涨) 入场=4453.05 出场=4455.05 出场时间=1756949400000 差价=-2.00 盈利=-4.49 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 10:15:00号 做空(跌包涨) 入场=4458.70 出场=4428.70 出场时间=1756953000000 差价=30.00 盈利=67.28 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 11:45:00号 做空(跌包涨) 入场=4396.51 出场=4398.51 出场时间=1756958400000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 12:15:00号 做多(涨包跌) 入场=4401.01 出场=4399.01 出场时间=1756960200000 差价=-2.00 盈利=-4.54 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 12:45:00号 做多(涨包跌) 入场=4403.00 出场=4401.00 出场时间=1756962000000 差价=-2.00 盈利=-4.54 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 13:00:00号 做空(跌包涨) 入场=4389.87 出场=4359.87 出场时间=1756966500000 差价=30.00 盈利=68.34 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 14:00:00号 做多(涨包跌) 入场=4372.82 出场=4370.82 出场时间=1756966500000 差价=-2.00 盈利=-4.57 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 14:30:00号 做空(跌包涨) 入场=4366.72 出场=4368.72 出场时间=1756969200000 差价=-2.00 盈利=-4.58 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 16:00:00号 做多(涨包跌) 入场=4368.80 出场=4366.80 出场时间=1756975500000 差价=-2.00 盈利=-4.58 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 16:45:00号 做空(跌包涨) 入场=4373.25 出场=4375.25 出场时间=1756976400000 差价=-2.00 盈利=-4.57 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 17:15:00号 做多(涨包跌) 入场=4380.98 出场=4378.98 出场时间=1756978200000 差价=-2.00 盈利=-4.57 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 17:45:00号 做多(涨包跌) 入场=4384.86 出场=4414.86 出场时间=1756980900000 差价=30.00 盈利=68.42 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 18:45:00号 做多(涨包跌) 入场=4419.81 出场=4417.81 出场时间=1756983600000 差价=-2.00 盈利=-4.53 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 19:00:00号 做空(跌包涨) 入场=4405.14 出场=4407.14 出场时间=1756984500000 差价=-2.00 盈利=-4.54 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 19:15:00号 做多(涨包跌) 入场=4423.54 出场=4421.54 出场时间=1756985400000 差价=-2.00 盈利=-4.52 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-04 20:00:00号 做空(跌包涨) 入场=4404.40 出场=4406.40 出场时间=1756988100000 差价=-2.00 盈利=-4.54 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-04 21:00:00号 做多(涨包跌) 入场=4414.04 出场=4412.04 出场时间=1756991700000 差价=-2.00 盈利=-4.53 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-04 21:45:00号 做空(跌包涨) 入场=4381.48 出场=4351.48 出场时间=1756994400000 差价=30.00 盈利=68.47 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-04 23:00:00号 做空(跌包涨) 入场=4339.69 出场=4309.69 出场时间=1756998900000 差价=30.00 盈利=69.13 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 00:15:00号 做多(涨包跌) 入场=4306.10 出场=4304.10 出场时间=1757003400000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 00:30:00号 做空(跌包涨) 入场=4287.51 出场=4289.51 出场时间=1757004300000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 01:15:00号 做多(涨包跌) 入场=4305.80 出场=4303.80 出场时间=1757007000000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 01:45:00号 做空(跌包涨) 入场=4301.01 出场=4303.01 出场时间=1757008800000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 02:30:00号 做空(跌包涨) 入场=4278.68 出场=4280.68 出场时间=1757011500000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 03:00:00号 做空(跌包涨) 入场=4271.51 出场=4273.51 出场时间=1757013300000 差价=-2.00 盈利=-4.68 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 04:15:00号 做多(涨包跌) 入场=4294.31 出场=4324.31 出场时间=1757024100000 差价=30.00 盈利=69.86 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 05:30:00号 做多(涨包跌) 入场=4309.65 出场=4339.65 出场时间=1757025900000 差价=30.00 盈利=69.61 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 06:45:00号 做空(跌包涨) 入场=4315.58 出场=4317.58 出场时间=1757026800000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 07:00:00号 做多(涨包跌) 入场=4339.52 出场=4337.52 出场时间=1757027700000 差价=-2.00 盈利=-4.61 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 09:00:00号 做空(跌包涨) 入场=4298.93 出场=4300.93 出场时间=1757034900000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 11:00:00号 做多(涨包跌) 入场=4321.58 出场=4319.58 出场时间=1757042100000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 11:15:00号 做空(跌包涨) 入场=4303.41 出场=4305.41 出场时间=1757043000000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 11:45:00号 做多(涨包跌) 入场=4312.51 出场=4342.51 出场时间=1757055600000 差价=30.00 盈利=69.57 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 13:45:00号 做多(涨包跌) 入场=4336.36 出场=4334.36 出场时间=1757052000000 差价=-2.00 盈利=-4.61 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-05 15:15:00号 做多(涨包跌) 入场=4354.09 出场=4384.09 出场时间=1757057400000 差价=30.00 盈利=68.90 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-05 17:15:00号 做空(跌包涨) 入场=4394.99 出场=4396.99 出场时间=1757064600000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-05 20:00:00号 做空(跌包涨) 入场=4415.56 出场=4417.56 出场时间=1757074500000 差价=-2.00 盈利=-4.53 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-05 21:00:00号 做多(涨包跌) 入场=4469.26 出场=4467.26 出场时间=1757078100000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 00:00:00号 做空(跌包涨) 入场=4280.44 出场=4282.44 出场时间=1757088900000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 00:45:00号 做多(涨包跌) 入场=4297.13 出场=4295.13 出场时间=1757091600000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 02:00:00号 做多(涨包跌) 入场=4287.32 出场=4285.32 出场时间=1757096100000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 02:30:00号 做多(涨包跌) 入场=4295.81 出场=4325.81 出场时间=1757099700000 差价=30.00 盈利=69.84 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 04:00:00号 做多(涨包跌) 入场=4328.97 出场=4326.97 出场时间=1757103300000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 04:45:00号 做多(涨包跌) 入场=4337.01 出场=4335.01 出场时间=1757106000000 差价=-2.00 盈利=-4.61 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 06:00:00号 做空(跌包涨) 入场=4294.51 出场=4296.51 出场时间=1757110500000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 06:45:00号 做多(涨包跌) 入场=4314.65 出场=4312.65 出场时间=1757113200000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 07:45:00号 做空(跌包涨) 入场=4308.19 出场=4310.19 出场时间=1757117700000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 08:30:00号 做多(涨包跌) 入场=4314.27 出场=4312.27 出场时间=1757122200000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 09:30:00号 做空(跌包涨) 入场=4313.82 出场=4315.82 出场时间=1757123100000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 13:00:00号 做多(涨包跌) 入场=4307.45 出场=4305.45 出场时间=1757135700000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 13:30:00号 做空(跌包涨) 入场=4295.99 出场=4297.99 出场时间=1757138400000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 14:15:00号 做多(涨包跌) 入场=4299.28 出场=4297.28 出场时间=1757149200000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 15:15:00号 做多(涨包跌) 入场=4306.50 出场=4304.50 出场时间=1757143800000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 15:45:00号 做多(涨包跌) 入场=4307.61 出场=4305.61 出场时间=1757145600000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 16:15:00号 做空(跌包涨) 入场=4305.07 出场=4307.07 出场时间=1757147400000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 17:45:00号 做多(涨包跌) 入场=4296.51 出场=4294.51 出场时间=1757152800000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 18:00:00号 做空(跌包涨) 入场=4291.09 出场=4293.09 出场时间=1757153700000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 18:45:00号 做多(涨包跌) 入场=4301.16 出场=4299.16 出场时间=1757156400000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 19:45:00号 做空(跌包涨) 入场=4293.71 出场=4295.71 出场时间=1757160000000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 20:00:00号 做多(涨包跌) 入场=4298.08 出场=4296.08 出场时间=1757160900000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 20:30:00号 做空(跌包涨) 入场=4297.58 出场=4299.58 出场时间=1757162700000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 21:45:00号 做空(跌包涨) 入场=4291.70 出场=4293.70 出场时间=1757167200000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 23:00:00号 做空(跌包涨) 入场=4294.19 出场=4264.19 出场时间=1757173500000 差价=30.00 盈利=69.86 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-07 00:30:00号 做空(跌包涨) 入场=4257.59 出场=4259.59 出场时间=1757177100000 差价=-2.00 盈利=-4.70 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-07 00:45:00号 做多(涨包跌) 入场=4271.27 出场=4269.27 出场时间=1757179800000 差价=-2.00 盈利=-4.68 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-07 03:00:00号 做多(涨包跌) 入场=4276.85 出场=4274.85 出场时间=1757186100000 差价=-2.00 盈利=-4.68 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-07 04:15:00号 做多(涨包跌) 入场=4260.10 出场=4290.10 出场时间=1757206800000 差价=30.00 盈利=70.42 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-07 05:45:00号 做空(跌包涨) 入场=4271.09 出场=4273.09 出场时间=1757196000000 差价=-2.00 盈利=-4.68 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-07 06:00:00号 做多(涨包跌) 入场=4281.89 出场=4279.89 出场时间=1757196900000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-07 07:00:00号 做多(涨包跌) 入场=4275.91 出场=4273.91 出场时间=1757200500000 差价=-2.00 盈利=-4.68 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-07 07:45:00号 做空(跌包涨) 入场=4273.27 出场=4275.27 出场时间=1757203200000 差价=-2.00 盈利=-4.68 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-07 08:15:00号 做多(涨包跌) 入场=4277.06 出场=4275.06 出场时间=1757205000000 差价=-2.00 盈利=-4.68 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.375 | INFO | __main__:<module>:216 - 2025-09-07 08:45:00号 做多(涨包跌) 入场=4283.66 出场=4281.66 出场时间=1757206800000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 09:15:00号 做多(涨包跌) 入场=4289.62 出场=4287.62 出场时间=1757210400000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 10:30:00号 做多(涨包跌) 入场=4293.75 出场=4291.75 出场时间=1757216700000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 11:00:00号 做多(涨包跌) 入场=4297.65 出场=4295.65 出场时间=1757214900000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 11:30:00号 做多(涨包跌) 入场=4300.79 出场=4298.79 出场时间=1757216700000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 13:00:00号 做多(涨包跌) 入场=4299.27 出场=4297.27 出场时间=1757222100000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 13:30:00号 做多(涨包跌) 入场=4300.38 出场=4298.38 出场时间=1757223900000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 13:45:00号 做空(跌包涨) 入场=4292.60 出场=4294.60 出场时间=1757230200000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 14:15:00号 做空(跌包涨) 入场=4288.76 出场=4290.76 出场时间=1757226600000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 14:45:00号 做空(跌包涨) 入场=4284.83 出场=4286.83 出场时间=1757229300000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 16:15:00号 做多(涨包跌) 入场=4299.99 出场=4297.99 出场时间=1757235600000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 17:15:00号 做多(涨包跌) 入场=4304.68 出场=4302.68 出场时间=1757237400000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 17:30:00号 做空(跌包涨) 入场=4299.22 出场=4301.22 出场时间=1757239200000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 18:30:00号 做空(跌包涨) 入场=4297.80 出场=4299.80 出场时间=1757241900000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 19:30:00号 做空(跌包涨) 入场=4301.86 出场=4303.86 出场时间=1757245500000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 20:15:00号 做空(跌包涨) 入场=4298.00 出场=4300.00 出场时间=1757248200000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 21:15:00号 做空(跌包涨) 入场=4294.46 出场=4296.46 出场时间=1757251800000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 22:45:00号 做多(涨包跌) 入场=4309.11 出场=4307.11 出场时间=1757257200000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 23:00:00号 做空(跌包涨) 入场=4298.01 出场=4300.01 出场时间=1757258100000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 23:30:00号 做多(涨包跌) 入场=4298.19 出场=4296.19 出场时间=1757259900000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 00:00:00号 做空(跌包涨) 入场=4297.13 出场=4299.13 出场时间=1757283300000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 01:00:00号 做空(跌包涨) 入场=4281.91 出场=4283.91 出场时间=1757271600000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 01:45:00号 做多(涨包跌) 入场=4281.14 出场=4279.14 出场时间=1757268000000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 03:30:00号 做多(涨包跌) 入场=4279.60 出场=4309.60 出场时间=1757286000000 差价=30.00 盈利=70.10 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 06:00:00号 做空(跌包涨) 入场=4286.10 出场=4288.10 出场时间=1757283300000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 06:15:00号 做多(涨包跌) 入场=4294.90 出场=4292.90 出场时间=1757284200000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 07:00:00号 做多(涨包跌) 入场=4308.01 出场=4306.01 出场时间=1757288700000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 08:30:00号 做多(涨包跌) 入场=4314.79 出场=4312.79 出场时间=1757292300000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 10:15:00号 做空(跌包涨) 入场=4289.77 出场=4291.77 出场时间=1757298600000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 10:45:00号 做多(涨包跌) 入场=4301.19 出场=4299.19 出场时间=1757304000000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 11:30:00号 做多(涨包跌) 入场=4307.14 出场=4305.14 出场时间=1757304000000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 12:45:00号 做空(跌包涨) 入场=4301.22 出场=4303.22 出场时间=1757315700000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 13:45:00号 做空(跌包涨) 入场=4287.64 出场=4289.64 出场时间=1757311200000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 14:30:00号 做多(涨包跌) 入场=4299.95 出场=4297.95 出场时间=1757313900000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 15:45:00号 做空(跌包涨) 入场=4293.26 出场=4295.26 出场时间=1757319300000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 17:15:00号 做多(涨包跌) 入场=4315.78 出场=4313.78 出场时间=1757323800000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 17:45:00号 做空(跌包涨) 入场=4310.70 出场=4312.70 出场时间=1757325600000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 19:00:00号 做空(跌包涨) 入场=4327.00 出场=4329.00 出场时间=1757336400000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 20:30:00号 做多(涨包跌) 入场=4313.57 出场=4343.57 出场时间=1757336400000 差价=30.00 盈利=69.55 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 21:15:00号 做多(涨包跌) 入场=4357.36 出场=4355.36 出场时间=1757338200000 差价=-2.00 盈利=-4.59 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-09 00:45:00号 做空(跌包涨) 入场=4345.02 出场=4315.02 出场时间=1757353500000 差价=30.00 盈利=69.04 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-09 01:30:00号 做多(涨包跌) 入场=4339.20 出场=4337.20 出场时间=1757353500000 差价=-2.00 盈利=-4.61 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-09 01:45:00号 做空(跌包涨) 入场=4323.36 出场=4325.36 出场时间=1757355300000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-09 03:00:00号 做多(涨包跌) 入场=4327.57 出场=4325.57 出场时间=1757358900000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-09 03:15:00号 做空(跌包涨) 入场=4312.48 出场=4282.48 出场时间=1757359800000 差价=30.00 盈利=69.57 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-09 06:30:00号 做空(跌包涨) 入场=4316.91 出场=4286.91 出场时间=1757377800000 差价=30.00 盈利=69.49 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-09 07:30:00号 做空(跌包涨) 入场=4301.76 出场=4303.76 出场时间=1757375100000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-09 08:00:00号 做多(涨包跌) 入场=4303.99 出场=4301.99 出场时间=1757376900000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 08:30:00号 做空(跌包涨) 入场=4298.46 出场=4300.46 出场时间=1757378700000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 09:00:00号 做多(涨包跌) 入场=4299.61 出场=4297.61 出场时间=1757380500000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 09:15:00号 做空(跌包涨) 入场=4296.12 出场=4298.12 出场时间=1757387700000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 10:00:00号 做空(跌包涨) 入场=4284.98 出场=4286.98 出场时间=1757384100000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 12:00:00号 做多(涨包跌) 入场=4304.98 出场=4302.98 出场时间=1757391300000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 12:30:00号 做多(涨包跌) 入场=4309.18 出场=4307.18 出场时间=1757393100000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 13:00:00号 做多(涨包跌) 入场=4311.76 出场=4309.76 出场时间=1757394900000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 13:15:00号 做空(跌包涨) 入场=4304.94 出场=4306.94 出场时间=1757395800000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 17:30:00号 做多(涨包跌) 入场=4364.44 出场=4362.44 出场时间=1757411100000 差价=-2.00 盈利=-4.58 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 18:30:00号 做空(跌包涨) 入场=4344.37 出场=4346.37 出场时间=1757414700000 差价=-2.00 盈利=-4.60 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 18:45:00号 做多(涨包跌) 入场=4357.24 出场=4355.24 出场时间=1757415600000 差价=-2.00 盈利=-4.59 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 20:15:00号 做多(涨包跌) 入场=4354.99 出场=4352.99 出场时间=1757421000000 差价=-2.00 盈利=-4.59 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 20:45:00号 做空(跌包涨) 入场=4346.93 出场=4348.93 出场时间=1757424600000 差价=-2.00 盈利=-4.60 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 23:15:00号 做空(跌包涨) 入场=4289.80 出场=4291.80 出场时间=1757432700000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 01:00:00号 做多(涨包跌) 入场=4286.31 出场=4284.31 出场时间=1757438100000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 01:30:00号 做空(跌包涨) 入场=4282.03 出场=4284.03 出场时间=1757439900000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 02:15:00号 做空(跌包涨) 入场=4283.63 出场=4285.63 出场时间=1757442600000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 02:30:00号 做多(涨包跌) 入场=4288.33 出场=4286.33 出场时间=1757443500000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 02:45:00号 做空(跌包涨) 入场=4280.97 出场=4282.97 出场时间=1757444400000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 04:15:00号 做空(跌包涨) 入场=4285.42 出场=4287.42 出场时间=1757449800000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 05:30:00号 做空(跌包涨) 入场=4297.59 出场=4299.59 出场时间=1757454300000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 06:45:00号 做多(涨包跌) 入场=4313.02 出场=4311.02 出场时间=1757458800000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 07:15:00号 做空(跌包涨) 入场=4310.76 出场=4312.76 出场时间=1757460600000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 07:45:00号 做多(涨包跌) 入场=4313.64 出场=4311.64 出场时间=1757462400000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 08:30:00号 做空(跌包涨) 入场=4296.21 出场=4298.21 出场时间=1757466900000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 09:00:00号 做空(跌包涨) 入场=4284.80 出场=4286.80 出场时间=1757466900000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 10:15:00号 做多(涨包跌) 入场=4311.47 出场=4309.47 出场时间=1757473200000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 11:00:00号 做空(跌包涨) 入场=4315.73 出场=4317.73 出场时间=1757474100000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 11:30:00号 做空(跌包涨) 入场=4308.70 出场=4310.70 出场时间=1757475900000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 13:15:00号 做空(跌包涨) 入场=4308.49 出场=4310.49 出场时间=1757482200000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 13:30:00号 做多(涨包跌) 入场=4312.14 出场=4310.14 出场时间=1757483100000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 14:00:00号 做空(跌包涨) 入场=4306.35 出场=4308.35 出场时间=1757484900000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 14:30:00号 做多(涨包跌) 入场=4308.01 出场=4338.01 出场时间=1757506500000 差价=30.00 盈利=69.64 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 15:15:00号 做多(涨包跌) 入场=4317.26 出场=4315.26 出场时间=1757494800000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 16:30:00号 做空(跌包涨) 入场=4327.00 出场=4329.00 出场时间=1757493900000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 17:15:00号 做空(跌包涨) 入场=4316.58 出场=4318.58 出场时间=1757496600000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 18:30:00号 做多(涨包跌) 入场=4327.20 出场=4325.20 出场时间=1757501100000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 19:00:00号 做空(跌包涨) 入场=4325.23 出场=4327.23 出场时间=1757502900000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 19:15:00号 做多(涨包跌) 入场=4330.73 出场=4328.73 出场时间=1757503800000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 20:00:00号 做多(涨包跌) 入场=4329.56 出场=4359.56 出场时间=1757507400000 差价=30.00 盈利=69.29 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 21:15:00号 做多(涨包跌) 入场=4382.99 出场=4380.99 出场时间=1757511000000 差价=-2.00 盈利=-4.56 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 21:45:00号 做多(涨包跌) 入场=4386.20 出场=4416.20 出场时间=1757512800000 差价=30.00 盈利=68.40 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.381 | INFO | __main__:<module>:216 - 2025-09-10 23:15:00号 做多(涨包跌) 入场=4421.51 出场=4419.51 出场时间=1757518200000 差价=-2.00 盈利=-4.52 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 00:00:00号 做空(跌包涨) 入场=4403.47 出场=4373.47 出场时间=1757520900000 差价=30.00 盈利=68.13 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 01:30:00号 做空(跌包涨) 入场=4346.68 出场=4348.68 出场时间=1757526300000 差价=-2.00 盈利=-4.60 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 05:15:00号 做多(涨包跌) 入场=4337.51 出场=4335.51 出场时间=1757539800000 差价=-2.00 盈利=-4.61 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 05:45:00号 做多(涨包跌) 入场=4346.90 出场=4344.90 出场时间=1757542500000 差价=-2.00 盈利=-4.60 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 06:30:00号 做空(跌包涨) 入场=4344.22 出场=4346.22 出场时间=1757544300000 差价=-2.00 盈利=-4.60 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 07:30:00号 做空(跌包涨) 入场=4342.82 出场=4344.82 出场时间=1757547900000 差价=-2.00 盈利=-4.61 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 08:15:00号 做空(跌包涨) 入场=4336.86 出场=4338.86 出场时间=1757550600000 差价=-2.00 盈利=-4.61 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 09:00:00号 做多(涨包跌) 入场=4357.50 出场=4387.50 出场时间=1757560500000 差价=30.00 盈利=68.85 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 10:15:00号 做多(涨包跌) 入场=4373.00 出场=4371.00 出场时间=1757557800000 差价=-2.00 盈利=-4.57 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 10:45:00号 做多(涨包跌) 入场=4379.99 出场=4377.99 出场时间=1757559600000 差价=-2.00 盈利=-4.57 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 12:00:00号 做多(涨包跌) 入场=4402.00 出场=4400.00 出场时间=1757567700000 差价=-2.00 盈利=-4.54 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 12:30:00号 做多(涨包跌) 入场=4411.97 出场=4409.97 出场时间=1757565900000 差价=-2.00 盈利=-4.53 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 13:00:00号 做空(跌包涨) 入场=4409.99 出场=4411.99 出场时间=1757567700000 差价=-2.00 盈利=-4.54 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 15:30:00号 做空(跌包涨) 入场=4435.47 出场=4437.47 出场时间=1757576700000 差价=-2.00 盈利=-4.51 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 16:45:00号 做多(涨包跌) 入场=4436.04 出场=4434.04 出场时间=1757581200000 差价=-2.00 盈利=-4.51 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 18:00:00号 做多(涨包跌) 入场=4415.02 出场=4445.02 出场时间=1757592000000 差价=30.00 盈利=67.95 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-11 19:00:00号 做多(涨包跌) 入场=4426.37 出场=4424.37 出场时间=1757590200000 差价=-2.00 盈利=-4.52 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-11 19:30:00号 做空(跌包涨) 入场=4425.53 出场=4427.53 出场时间=1757591100000 差价=-2.00 盈利=-4.52 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-11 19:45:00号 做多(涨包跌) 入场=4434.28 出场=4432.28 出场时间=1757592000000 差价=-2.00 盈利=-4.51 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-11 20:15:00号 做多(涨包跌) 入场=4452.46 出场=4450.46 出场时间=1757593800000 差价=-2.00 盈利=-4.49 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-11 20:45:00号 做空(跌包涨) 入场=4413.23 出场=4383.23 出场时间=1757595600000 差价=30.00 盈利=67.98 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-11 21:15:00号 做空(跌包涨) 入场=4390.85 出场=4392.85 出场时间=1757597400000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-11 22:00:00号 做多(涨包跌) 入场=4434.65 出场=4432.65 出场时间=1757600100000 差价=-2.00 盈利=-4.51 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-11 22:45:00号 做多(涨包跌) 入场=4428.48 出场=4426.48 出场时间=1757602800000 差价=-2.00 盈利=-4.52 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 00:30:00号 做空(跌包涨) 入场=4409.53 出场=4411.53 出场时间=1757609100000 差价=-2.00 盈利=-4.54 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 01:15:00号 做多(涨包跌) 入场=4428.67 出场=4426.67 出场时间=1757611800000 差价=-2.00 盈利=-4.52 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 01:45:00号 做多(涨包跌) 入场=4434.41 出场=4432.41 出场时间=1757613600000 差价=-2.00 盈利=-4.51 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 03:30:00号 做多(涨包跌) 入场=4421.36 出场=4419.36 出场时间=1757620800000 差价=-2.00 盈利=-4.52 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 05:45:00号 做多(涨包跌) 入场=4421.45 出场=4451.45 出场时间=1757630700000 差价=30.00 盈利=67.85 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 06:30:00号 做多(涨包跌) 入场=4441.44 出场=4471.44 出场时间=1757637000000 差价=30.00 盈利=67.55 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 08:30:00号 做多(涨包跌) 入场=4461.82 出场=4491.82 出场时间=1757637900000 差价=30.00 盈利=67.24 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 12:00:00号 做多(涨包跌) 入场=4503.64 出场=4533.64 出场时间=1757655900000 差价=30.00 盈利=66.61 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 13:15:00号 做空(跌包涨) 入场=4514.86 出场=4516.86 出场时间=1757655000000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 15:15:00号 做多(涨包跌) 入场=4544.41 出场=4542.41 出场时间=1757662200000 差价=-2.00 盈利=-4.40 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 15:30:00号 做空(跌包涨) 入场=4537.01 出场=4507.01 出场时间=1757670300000 差价=30.00 盈利=66.12 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 17:15:00号 做多(涨包跌) 入场=4522.01 出场=4520.01 出场时间=1757669400000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 17:30:00号 做空(跌包涨) 入场=4516.28 出场=4518.28 出场时间=1757670300000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-12 18:00:00号 做空(跌包涨) 入场=4513.69 出场=4515.69 出场时间=1757672100000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-12 18:30:00号 做空(跌包涨) 入场=4509.80 出场=4511.80 出场时间=1757673900000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-12 21:00:00号 做多(涨包跌) 入场=4524.96 出场=4522.96 出场时间=1757682900000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-12 22:00:00号 做多(涨包跌) 入场=4537.98 出场=4567.98 出场时间=1757686500000 差价=30.00 盈利=66.11 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-12 22:45:00号 做空(跌包涨) 入场=4523.25 出场=4525.25 出场时间=1757689200000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-12 23:45:00号 做多(涨包跌) 入场=4539.35 出场=4537.35 出场时间=1757692800000 差价=-2.00 盈利=-4.41 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-13 01:00:00号 做多(涨包跌) 入场=4573.66 出场=4571.66 出场时间=1757697300000 差价=-2.00 盈利=-4.37 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-13 02:30:00号 做多(涨包跌) 入场=4612.59 出场=4610.59 出场时间=1757702700000 差价=-2.00 盈利=-4.34 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-13 03:30:00号 做多(涨包跌) 入场=4627.09 出场=4657.09 出场时间=1757706300000 差价=30.00 盈利=64.84 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-13 04:15:00号 做空(跌包涨) 入场=4640.52 出场=4642.52 出场时间=1757709000000 差价=-2.00 盈利=-4.31 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-13 06:15:00号 做多(涨包跌) 入场=4662.69 出场=4692.69 出场时间=1757716200000 差价=30.00 盈利=64.34 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-13 07:30:00号 做多(涨包跌) 入场=4705.12 出场=4703.12 出场时间=1757720700000 差价=-2.00 盈利=-4.25 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.385 | INFO | __main__:<module>:216 - 2025-09-13 08:00:00号 做多(涨包跌) 入场=4710.83 出场=4708.83 出场时间=1757722500000 差价=-2.00 盈利=-4.25 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 08:45:00号 做多(涨包跌) 入场=4699.24 出场=4729.24 出场时间=1757725200000 差价=30.00 盈利=63.84 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 11:15:00号 做多(涨包跌) 入场=4711.38 出场=4741.38 出场时间=1757734200000 差价=30.00 盈利=63.68 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 12:15:00号 做空(跌包涨) 入场=4736.89 出场=4738.89 出场时间=1757737800000 差价=-2.00 盈利=-4.22 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 12:45:00号 做多(涨包跌) 入场=4745.06 出场=4743.06 出场时间=1757739600000 差价=-2.00 盈利=-4.21 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 14:15:00号 做多(涨包跌) 入场=4726.83 出场=4724.83 出场时间=1757745000000 差价=-2.00 盈利=-4.23 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 14:30:00号 做空(跌包涨) 入场=4713.13 出场=4715.13 出场时间=1757747700000 差价=-2.00 盈利=-4.24 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 15:15:00号 做多(涨包跌) 入场=4712.78 出场=4710.78 出场时间=1757749500000 差价=-2.00 盈利=-4.24 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 16:15:00号 做多(涨包跌) 入场=4716.82 出场=4714.82 出场时间=1757759400000 差价=-2.00 盈利=-4.24 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 16:45:00号 做多(涨包跌) 入场=4722.10 出场=4720.10 出场时间=1757754900000 差价=-2.00 盈利=-4.24 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 18:30:00号 做空(跌包涨) 入场=4718.32 出场=4720.32 出场时间=1757763000000 差价=-2.00 盈利=-4.24 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 21:30:00号 做多(涨包跌) 入场=4722.50 出场=4720.50 出场时间=1757771100000 差价=-2.00 盈利=-4.24 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 21:45:00号 做空(跌包涨) 入场=4710.16 出场=4680.16 出场时间=1757772000000 差价=30.00 盈利=63.69 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 22:30:00号 做多(涨包跌) 入场=4699.16 出场=4697.16 出场时间=1757774700000 差价=-2.00 盈利=-4.26 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 23:30:00号 做空(跌包涨) 入场=4679.37 出场=4649.37 出场时间=1757778300000 差价=30.00 盈利=64.11 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-14 04:00:00号 做空(跌包涨) 入场=4635.56 出场=4637.56 出场时间=1757794500000 差价=-2.00 盈利=-4.31 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-14 04:15:00号 做多(涨包跌) 入场=4655.26 出场=4653.26 出场时间=1757795400000 差价=-2.00 盈利=-4.30 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-14 05:00:00号 做多(涨包跌) 入场=4655.22 出场=4653.22 出场时间=1757801700000 差价=-2.00 盈利=-4.30 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-14 06:00:00号 做多(涨包跌) 入场=4661.08 出场=4659.08 出场时间=1757801700000 差价=-2.00 盈利=-4.29 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 07:00:00号 做多(涨包跌) 入场=4656.48 出场=4654.48 出场时间=1757806200000 差价=-2.00 盈利=-4.30 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 08:00:00号 做多(涨包跌) 入场=4664.77 出场=4662.77 出场时间=1757810700000 差价=-2.00 盈利=-4.29 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 09:15:00号 做空(跌包涨) 入场=4653.35 出场=4655.35 出场时间=1757813400000 差价=-2.00 盈利=-4.30 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 11:00:00号 做多(涨包跌) 入场=4681.64 出场=4679.64 出场时间=1757819700000 差价=-2.00 盈利=-4.27 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 11:15:00号 做空(跌包涨) 入场=4673.93 出场=4643.93 出场时间=1757823300000 差价=30.00 盈利=64.19 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 13:15:00号 做多(涨包跌) 入场=4666.86 出场=4664.86 出场时间=1757829600000 差价=-2.00 盈利=-4.29 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 14:15:00号 做空(跌包涨) 入场=4668.51 出场=4670.51 出场时间=1757841300000 差价=-2.00 盈利=-4.28 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 14:45:00号 做空(跌包涨) 入场=4660.54 出场=4662.54 出场时间=1757833200000 差价=-2.00 盈利=-4.29 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 15:15:00号 做空(跌包涨) 入场=4659.38 出场=4661.38 出场时间=1757835000000 差价=-2.00 盈利=-4.29 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 15:45:00号 做空(跌包涨) 入场=4658.02 出场=4660.02 出场时间=1757838600000 差价=-2.00 盈利=-4.29 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 17:00:00号 做空(跌包涨) 入场=4654.18 出场=4656.18 出场时间=1757841300000 差价=-2.00 盈利=-4.30 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 18:00:00号 做空(跌包涨) 入场=4663.76 出场=4665.76 出场时间=1757844900000 差价=-2.00 盈利=-4.29 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 19:30:00号 做空(跌包涨) 入场=4638.18 出场=4640.18 出场时间=1757852100000 差价=-2.00 盈利=-4.31 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 20:30:00号 做多(涨包跌) 入场=4640.66 出场=4638.66 出场时间=1757853900000 差价=-2.00 盈利=-4.31 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 20:45:00号 做空(跌包涨) 入场=4625.76 出场=4627.76 出场时间=1757854800000 差价=-2.00 盈利=-4.32 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 21:15:00号 做空(跌包涨) 入场=4621.87 出场=4623.87 出场时间=1757856600000 差价=-2.00 盈利=-4.33 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 22:00:00号 做空(跌包涨) 入场=4610.66 出场=4612.66 出场时间=1757859300000 差价=-2.00 盈利=-4.34 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 22:15:00号 做多(涨包跌) 入场=4630.71 出场=4628.71 出场时间=1757860200000 差价=-2.00 盈利=-4.32 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 23:15:00号 做空(跌包涨) 入场=4586.03 出场=4588.03 出场时间=1757863800000 差价=-2.00 盈利=-4.36 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 23:45:00号 做空(跌包涨) 入场=4584.48 出场=4586.48 出场时间=1757865600000 差价=-2.00 盈利=-4.36 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 00:15:00号 做多(涨包跌) 入场=4586.68 出场=4616.68 出场时间=1757870100000 差价=30.00 盈利=65.41 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 01:00:00号 做空(跌包涨) 入场=4595.18 出场=4597.18 出场时间=1757870100000 差价=-2.00 盈利=-4.35 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 03:15:00号 做多(涨包跌) 入场=4609.78 出场=4607.78 出场时间=1757883600000 差价=-2.00 盈利=-4.34 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 04:00:00号 做空(跌包涨) 入场=4613.99 出场=4615.99 出场时间=1757880900000 差价=-2.00 盈利=-4.33 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 04:15:00号 做多(涨包跌) 入场=4624.18 出场=4622.18 出场时间=1757881800000 差价=-2.00 盈利=-4.33 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 05:00:00号 做空(跌包涨) 入场=4614.96 出场=4616.96 出场时间=1757884500000 差价=-2.00 盈利=-4.33 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 05:30:00号 做空(跌包涨) 入场=4613.57 出场=4615.57 出场时间=1757886300000 差价=-2.00 盈利=-4.34 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 05:45:00号 做多(涨包跌) 入场=4633.00 出场=4631.00 出场时间=1757887200000 差价=-2.00 盈利=-4.32 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 08:30:00号 做多(涨包跌) 入场=4613.20 出场=4611.20 出场时间=1757897100000 差价=-2.00 盈利=-4.34 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 09:30:00号 做多(涨包跌) 入场=4601.91 出场=4631.91 出场时间=1757905200000 差价=30.00 盈利=65.19 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 10:30:00号 做空(跌包涨) 入场=4615.91 出场=4617.91 出场时间=1757904300000 差价=-2.00 盈利=-4.33 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 11:15:00号 做多(涨包跌) 入场=4630.48 出场=4628.48 出场时间=1757907000000 差价=-2.00 盈利=-4.32 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 12:00:00号 做多(涨包跌) 入场=4634.46 出场=4664.46 出场时间=1757916000000 差价=30.00 盈利=64.73 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 13:15:00号 做多(涨包跌) 入场=4648.29 出场=4646.29 出场时间=1757917800000 差价=-2.00 盈利=-4.30 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 14:00:00号 做多(涨包跌) 入场=4660.51 出场=4658.51 出场时间=1757916900000 差价=-2.00 盈利=-4.29 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 14:30:00号 做空(跌包涨) 入场=4649.31 出场=4619.31 出场时间=1757918700000 差价=30.00 盈利=64.53 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-15 15:30:00号 做空(跌包涨) 入场=4623.51 出场=4593.51 出场时间=1757922300000 差价=30.00 盈利=64.89 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-15 19:30:00号 做空(跌包涨) 入场=4527.63 出场=4529.63 出场时间=1757936700000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-15 20:30:00号 做空(跌包涨) 入场=4512.65 出场=4514.65 出场时间=1757940300000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-15 21:45:00号 做空(跌包涨) 入场=4495.32 出场=4497.32 出场时间=1757944800000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-15 22:30:00号 做多(涨包跌) 入场=4525.84 出场=4523.84 出场时间=1757947500000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-16 00:00:00号 做多(涨包跌) 入场=4497.47 出场=4495.47 出场时间=1757952900000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-16 00:30:00号 做空(跌包涨) 入场=4490.54 出场=4492.54 出场时间=1757954700000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-16 00:45:00号 做多(涨包跌) 入场=4508.90 出场=4506.90 出场时间=1757955600000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-16 01:30:00号 做空(跌包涨) 入场=4484.02 出场=4486.02 出场时间=1757958300000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-16 02:15:00号 做空(跌包涨) 入场=4492.05 出场=4494.05 出场时间=1757961000000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-16 03:00:00号 做多(涨包跌) 入场=4499.06 出场=4497.06 出场时间=1757963700000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-16 04:15:00号 做多(涨包跌) 入场=4499.05 出场=4529.05 出场时间=1757985300000 差价=30.00 盈利=66.68 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-16 05:15:00号 做多(涨包跌) 入场=4508.91 出场=4506.91 出场时间=1757977200000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-16 06:15:00号 做空(跌包涨) 入场=4519.44 出场=4521.44 出场时间=1757978100000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-16 07:15:00号 做多(涨包跌) 入场=4517.10 出场=4515.10 出场时间=1757980800000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 08:15:00号 做空(跌包涨) 入场=4514.54 出场=4516.54 出场时间=1757984400000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 10:15:00号 做多(涨包跌) 入场=4525.99 出场=4523.99 出场时间=1757989800000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 14:30:00号 做空(跌包涨) 入场=4514.40 出场=4516.40 出场时间=1758009600000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 15:15:00号 做空(跌包涨) 入场=4503.94 出场=4505.94 出场时间=1758007800000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 15:30:00号 做多(涨包跌) 入场=4508.98 出场=4506.98 出场时间=1758011400000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 16:00:00号 做多(涨包跌) 入场=4510.63 出场=4508.63 出场时间=1758011400000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 18:00:00号 做多(涨包跌) 入场=4509.35 出场=4507.35 出场时间=1758017700000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 19:30:00号 做多(涨包跌) 入场=4498.85 出场=4496.85 出场时间=1758023100000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 20:00:00号 做多(涨包跌) 入场=4499.40 出场=4497.40 出场时间=1758024900000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 20:45:00号 做多(涨包跌) 入场=4499.52 出场=4497.52 出场时间=1758027600000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 21:30:00号 做多(涨包跌) 入场=4506.71 出场=4504.71 出场时间=1758030300000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 21:45:00号 做空(跌包涨) 入场=4452.91 出场=4422.91 出场时间=1758031200000 差价=30.00 盈利=67.37 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 22:15:00号 做空(跌包涨) 入场=4432.50 出场=4434.50 出场时间=1758033000000 差价=-2.00 盈利=-4.51 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 23:15:00号 做空(跌包涨) 入场=4431.53 出场=4433.53 出场时间=1758036600000 差价=-2.00 盈利=-4.51 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-17 00:45:00号 做空(跌包涨) 入场=4456.88 出场=4458.88 出场时间=1758042000000 差价=-2.00 盈利=-4.49 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-17 01:00:00号 做多(涨包跌) 入场=4461.10 出场=4491.10 出场时间=1758042900000 差价=30.00 盈利=67.25 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 02:30:00号 做空(跌包涨) 入场=4481.50 出场=4483.50 出场时间=1758050100000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 05:15:00号 做空(跌包涨) 入场=4490.49 出场=4492.49 出场时间=1758058200000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 06:30:00号 做空(跌包涨) 入场=4505.59 出场=4507.59 出场时间=1758062700000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 06:45:00号 做多(涨包跌) 入场=4517.88 出场=4515.88 出场时间=1758063600000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 08:15:00号 做空(跌包涨) 入场=4495.76 出场=4497.76 出场时间=1758069000000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 08:30:00号 做多(涨包跌) 入场=4509.69 出场=4507.69 出场时间=1758069900000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 09:30:00号 做多(涨包跌) 入场=4514.84 出场=4512.84 出场时间=1758073500000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 09:45:00号 做空(跌包涨) 入场=4504.59 出场=4506.59 出场时间=1758074400000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 10:30:00号 做多(涨包跌) 入场=4514.12 出场=4544.12 出场时间=1758077100000 差价=30.00 盈利=66.46 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 12:30:00号 做空(跌包涨) 入场=4485.88 出场=4487.88 出场时间=1758085200000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 13:15:00号 做多(涨包跌) 入场=4484.36 出场=4482.36 出场时间=1758087000000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 13:45:00号 做多(涨包跌) 入场=4488.67 出场=4518.67 出场时间=1758089700000 差价=30.00 盈利=66.83 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 15:30:00号 做空(跌包涨) 入场=4539.99 出场=4541.99 出场时间=1758095100000 差价=-2.00 盈利=-4.41 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 16:00:00号 做多(涨包跌) 入场=4542.30 出场=4540.30 出场时间=1758096900000 差价=-2.00 盈利=-4.40 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 16:15:00号 做空(跌包涨) 入场=4515.06 出场=4485.06 出场时间=1758104100000 差价=30.00 盈利=66.44 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 17:00:00号 做多(涨包跌) 入场=4509.28 出场=4507.28 出场时间=1758100500000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 17:45:00号 做空(跌包涨) 入场=4501.69 出场=4503.69 出场时间=1758111300000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-17 19:00:00号 做空(跌包涨) 入场=4482.16 出场=4484.16 出场时间=1758107700000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-17 19:15:00号 做多(涨包跌) 入场=4487.54 出场=4485.54 出场时间=1758110400000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-17 20:15:00号 做空(跌包涨) 入场=4488.35 出场=4490.35 出场时间=1758112200000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-17 20:30:00号 做多(涨包跌) 入场=4504.63 出场=4502.63 出场时间=1758113100000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-17 22:30:00号 做空(跌包涨) 入场=4472.54 出场=4474.54 出场时间=1758120300000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-17 23:30:00号 做空(跌包涨) 入场=4480.00 出场=4482.00 出场时间=1758123900000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-18 01:15:00号 做空(跌包涨) 入场=4479.93 出场=4481.93 出场时间=1758130200000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-18 02:00:00号 做空(跌包涨) 入场=4436.71 出场=4438.71 出场时间=1758132900000 差价=-2.00 盈利=-4.51 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-18 02:45:00号 做空(跌包涨) 入场=4440.70 出场=4442.70 出场时间=1758135600000 差价=-2.00 盈利=-4.50 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-18 04:30:00号 做空(跌包涨) 入场=4512.43 出场=4514.43 出场时间=1758141900000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-18 05:15:00号 做多(涨包跌) 入场=4515.31 出场=4545.31 出场时间=1758146400000 差价=30.00 盈利=66.44 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-18 05:45:00号 做多(涨包跌) 入场=4528.32 出场=4526.32 出场时间=1758146400000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-18 06:15:00号 做多(涨包跌) 入场=4546.07 出场=4576.07 出场时间=1758149100000 差价=30.00 盈利=65.99 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-18 08:00:00号 做多(涨包跌) 入场=4588.29 出场=4586.29 出场时间=1758156300000 差价=-2.00 盈利=-4.36 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-18 09:00:00号 做空(跌包涨) 入场=4582.62 出场=4584.62 出场时间=1758158100000 差价=-2.00 盈利=-4.36 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-18 09:15:00号 做多(涨包跌) 入场=4602.25 出场=4632.25 出场时间=1758164400000 差价=30.00 盈利=65.19 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-18 12:30:00号 做空(跌包涨) 入场=4605.82 出场=4607.82 出场时间=1758170700000 差价=-2.00 盈利=-4.34 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.393 | INFO | __main__:<module>:216 - 2025-09-18 13:00:00号 做多(涨包跌) 入场=4609.48 出场=4607.48 出场时间=1758172500000 差价=-2.00 盈利=-4.34 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.393 | INFO | __main__:<module>:216 - 2025-09-18 13:15:00号 做空(跌包涨) 入场=4600.42 出场=4570.42 出场时间=1758175200000 差价=30.00 盈利=65.21 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.393 | INFO | __main__:<module>:216 - 2025-09-18 14:15:00号 做空(跌包涨) 入场=4561.37 出场=4563.37 出场时间=1758177000000 差价=-2.00 盈利=-4.38 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 14:45:00号 做多(涨包跌) 入场=4566.29 出场=4596.29 出场时间=1758184200000 差价=30.00 盈利=65.70 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 16:00:00号 做空(跌包涨) 入场=4573.09 出场=4575.09 出场时间=1758183300000 差价=-2.00 盈利=-4.37 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 16:15:00号 做多(涨包跌) 入场=4581.08 出场=4579.08 出场时间=1758190500000 差价=-2.00 盈利=-4.37 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 17:45:00号 做空(跌包涨) 入场=4590.39 出场=4592.39 出场时间=1758189600000 差价=-2.00 盈利=-4.36 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 18:15:00号 做空(跌包涨) 入场=4585.79 出场=4587.79 出场时间=1758202200000 差价=-2.00 盈利=-4.36 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 19:15:00号 做多(涨包跌) 入场=4579.55 出场=4577.55 出场时间=1758195000000 差价=-2.00 盈利=-4.37 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 19:45:00号 做空(跌包涨) 入场=4573.40 出场=4575.40 出场时间=1758196800000 差价=-2.00 盈利=-4.37 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 20:45:00号 做空(跌包涨) 入场=4576.54 出场=4578.54 出场时间=1758200400000 差价=-2.00 盈利=-4.37 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 21:15:00号 做多(涨包跌) 入场=4579.42 出场=4577.42 出场时间=1758202200000 差价=-2.00 盈利=-4.37 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 21:45:00号 做空(跌包涨) 入场=4571.96 出场=4573.96 出场时间=1758204000000 差价=-2.00 盈利=-4.37 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 22:00:00号 做多(涨包跌) 入场=4586.05 出场=4616.05 出场时间=1758204900000 差价=30.00 盈利=65.42 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 22:45:00号 做空(跌包涨) 入场=4598.00 出场=4600.00 出场时间=1758207600000 差价=-2.00 盈利=-4.35 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-19 00:30:00号 做多(涨包跌) 入场=4613.82 出场=4611.82 出场时间=1758214800000 差价=-2.00 盈利=-4.33 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-19 02:00:00号 做多(涨包跌) 入场=4607.20 出场=4605.20 出场时间=1758219300000 差价=-2.00 盈利=-4.34 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-19 03:00:00号 做多(涨包跌) 入场=4616.17 出场=4614.17 出场时间=1758222900000 差价=-2.00 盈利=-4.33 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-19 03:15:00号 做空(跌包涨) 入场=4604.49 出场=4574.49 出场时间=1758236400000 差价=30.00 盈利=65.15 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-19 04:00:00号 做空(跌包涨) 入场=4586.07 出场=4588.07 出场时间=1758226500000 差价=-2.00 盈利=-4.36 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-19 04:15:00号 做多(涨包跌) 入场=4594.26 出场=4592.26 出场时间=1758227400000 差价=-2.00 盈利=-4.35 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-19 04:45:00号 做多(涨包跌) 入场=4600.84 出场=4598.84 出场时间=1758229200000 差价=-2.00 盈利=-4.35 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 05:15:00号 做空(跌包涨) 入场=4592.22 出场=4594.22 出场时间=1758231000000 差价=-2.00 盈利=-4.36 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 06:00:00号 做空(跌包涨) 入场=4592.68 出场=4594.68 出场时间=1758233700000 差价=-2.00 盈利=-4.35 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 06:30:00号 做多(涨包跌) 入场=4593.16 出场=4591.16 出场时间=1758235500000 差价=-2.00 盈利=-4.35 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 07:00:00号 做空(跌包涨) 入场=4589.04 出场=4591.04 出场时间=1758240000000 差价=-2.00 盈利=-4.36 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 07:45:00号 做多(涨包跌) 入场=4583.92 出场=4613.92 出场时间=1758244500000 差价=30.00 盈利=65.45 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 09:30:00号 做多(涨包跌) 入场=4614.98 出场=4612.98 出场时间=1758246300000 差价=-2.00 盈利=-4.33 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 10:15:00号 做空(跌包涨) 入场=4596.00 出场=4598.00 出场时间=1758249000000 差价=-2.00 盈利=-4.35 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 11:00:00号 做空(跌包涨) 入场=4586.87 出场=4588.87 出场时间=1758251700000 差价=-2.00 盈利=-4.36 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 11:15:00号 做多(涨包跌) 入场=4594.12 出场=4592.12 出场时间=1758252600000 差价=-2.00 盈利=-4.35 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 11:30:00号 做空(跌包涨) 入场=4575.51 出场=4545.51 出场时间=1758253500000 差价=30.00 盈利=65.57 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 14:15:00号 做空(跌包涨) 入场=4531.27 出场=4533.27 出场时间=1758263400000 差价=-2.00 盈利=-4.41 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 14:45:00号 做空(跌包涨) 入场=4531.00 出场=4533.00 出场时间=1758266100000 差价=-2.00 盈利=-4.41 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 16:15:00号 做多(涨包跌) 入场=4540.60 出场=4538.60 出场时间=1758270600000 差价=-2.00 盈利=-4.40 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 17:15:00号 做多(涨包跌) 入场=4539.75 出场=4537.75 出场时间=1758274200000 差价=-2.00 盈利=-4.41 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 17:30:00号 做空(跌包涨) 入场=4529.53 出场=4531.53 出场时间=1758287700000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-19 18:15:00号 做多(涨包跌) 入场=4526.68 出场=4524.68 出场时间=1758277800000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-19 18:45:00号 做多(涨包跌) 入场=4529.15 出场=4527.15 出场时间=1758279600000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-19 20:30:00号 做空(跌包涨) 入场=4511.06 出场=4513.06 出场时间=1758285900000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-19 20:45:00号 做多(涨包跌) 入场=4516.78 出场=4514.78 出场时间=1758288600000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-19 21:15:00号 做多(涨包跌) 入场=4524.99 出场=4522.99 出场时间=1758288600000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-19 22:00:00号 做多(涨包跌) 入场=4525.81 出场=4523.81 出场时间=1758292200000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 00:15:00号 做多(涨包跌) 入场=4480.10 出场=4478.10 出场时间=1758299400000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 01:00:00号 做空(跌包涨) 入场=4478.90 出场=4448.90 出场时间=1758304800000 差价=30.00 盈利=66.98 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 03:00:00号 做空(跌包涨) 入场=4446.67 出场=4448.67 出场时间=1758309300000 差价=-2.00 盈利=-4.50 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 03:15:00号 做多(涨包跌) 入场=4457.43 出场=4455.43 出场时间=1758310200000 差价=-2.00 盈利=-4.49 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 03:45:00号 做空(跌包涨) 入场=4450.00 出场=4452.00 出场时间=1758312000000 差价=-2.00 盈利=-4.49 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 05:00:00号 做空(跌包涨) 入场=4457.40 出场=4459.40 出场时间=1758316500000 差价=-2.00 盈利=-4.49 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 07:45:00号 做多(涨包跌) 入场=4457.59 出场=4455.59 出场时间=1758331800000 差价=-2.00 盈利=-4.49 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 08:30:00号 做空(跌包涨) 入场=4460.41 出场=4462.41 出场时间=1758329100000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 08:45:00号 做多(涨包跌) 入场=4470.38 出场=4468.38 出场时间=1758330000000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 09:15:00号 做空(跌包涨) 入场=4464.23 出场=4466.23 出场时间=1758331800000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 11:45:00号 做多(涨包跌) 入场=4463.37 出场=4461.37 出场时间=1758340800000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 12:30:00号 做空(跌包涨) 入场=4462.81 出场=4464.81 出场时间=1758343500000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 12:45:00号 做多(涨包跌) 入场=4471.24 出场=4469.24 出场时间=1758344400000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 14:00:00号 做多(涨包跌) 入场=4476.31 出场=4474.31 出场时间=1758348900000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 14:15:00号 做空(跌包涨) 入场=4468.99 出场=4470.99 出场时间=1758349800000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 15:30:00号 做空(跌包涨) 入场=4468.61 出场=4470.61 出场时间=1758354300000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 15:45:00号 做多(涨包跌) 入场=4473.80 出场=4471.80 出场时间=1758357900000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 16:45:00号 做多(涨包跌) 入场=4474.98 出场=4472.98 出场时间=1758358800000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 17:00:00号 做空(跌包涨) 入场=4463.04 出场=4465.04 出场时间=1758359700000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-20 17:30:00号 做空(跌包涨) 入场=4463.00 出场=4465.00 出场时间=1758361500000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-20 18:00:00号 做多(涨包跌) 入场=4463.69 出场=4461.69 出场时间=1758365100000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-20 19:15:00号 做多(涨包跌) 入场=4467.67 出场=4465.67 出场时间=1758367800000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-20 21:30:00号 做多(涨包跌) 入场=4467.02 出场=4465.02 出场时间=1758376800000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-20 22:00:00号 做空(跌包涨) 入场=4466.11 出场=4468.11 出场时间=1758378600000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-21 00:15:00号 做多(涨包跌) 入场=4497.37 出场=4495.37 出场时间=1758385800000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-21 00:30:00号 做空(跌包涨) 入场=4492.10 出场=4494.10 出场时间=1758386700000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-21 00:45:00号 做多(涨包跌) 入场=4502.10 出场=4500.10 出场时间=1758387600000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-21 02:15:00号 做空(跌包涨) 入场=4479.14 出场=4481.14 出场时间=1758393000000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-21 05:00:00号 做空(跌包涨) 入场=4481.76 出场=4483.76 出场时间=1758402900000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-21 05:15:00号 做多(涨包跌) 入场=4489.11 出场=4487.11 出场时间=1758404700000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-21 06:45:00号 做空(跌包涨) 入场=4487.79 出场=4489.79 出场时间=1758409200000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-21 07:15:00号 做空(跌包涨) 入场=4486.31 出场=4456.31 出场时间=1758420900000 差价=30.00 盈利=66.87 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-21 08:15:00号 做多(涨包跌) 入场=4480.64 出场=4478.64 出场时间=1758414600000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-21 08:30:00号 做空(跌包涨) 入场=4474.88 出场=4476.88 出场时间=1758415500000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-21 08:45:00号 做多(涨包跌) 入场=4480.98 出场=4478.98 出场时间=1758418200000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 09:45:00号 做空(跌包涨) 入场=4479.33 出场=4481.33 出场时间=1758420000000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 10:15:00号 做多(涨包跌) 入场=4484.00 出场=4482.00 出场时间=1758421800000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 10:30:00号 做空(跌包涨) 入场=4473.56 出场=4475.56 出场时间=1758422700000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 12:00:00号 做多(涨包跌) 入场=4476.07 出场=4474.07 出场时间=1758428100000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 12:15:00号 做空(跌包涨) 入场=4468.37 出场=4470.37 出场时间=1758429000000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 12:45:00号 做多(涨包跌) 入场=4469.16 出场=4467.16 出场时间=1758430800000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 13:15:00号 做空(跌包涨) 入场=4465.52 出场=4467.52 出场时间=1758432600000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 14:45:00号 做多(涨包跌) 入场=4489.61 出场=4487.61 出场时间=1758438000000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 16:00:00号 做空(跌包涨) 入场=4475.86 出场=4445.86 出场时间=1758447000000 差价=30.00 盈利=67.03 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 20:00:00号 做空(跌包涨) 入场=4458.25 出场=4460.25 出场时间=1758456900000 差价=-2.00 盈利=-4.49 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 20:15:00号 做多(涨包跌) 入场=4463.00 出场=4461.00 出场时间=1758458700000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 20:45:00号 做空(跌包涨) 入场=4462.99 出场=4464.99 出场时间=1758459600000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 22:00:00号 做多(涨包跌) 入场=4473.99 出场=4471.99 出场时间=1758465000000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 22:30:00号 做空(跌包涨) 入场=4473.49 出场=4475.49 出场时间=1758465900000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 22:45:00号 做多(涨包跌) 入场=4477.45 出场=4475.45 出场时间=1758466800000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 23:00:00号 做空(跌包涨) 入场=4470.65 出场=4472.65 出场时间=1758467700000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 23:15:00号 做多(涨包跌) 入场=4480.89 出场=4478.89 出场时间=1758468600000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 23:45:00号 做多(涨包跌) 入场=4482.23 出场=4480.23 出场时间=1758471300000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-22 00:45:00号 做空(跌包涨) 入场=4468.98 出场=4470.98 出场时间=1758474000000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 02:45:00号 做多(涨包跌) 入场=4490.40 出场=4488.40 出场时间=1758481200000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 03:15:00号 做空(跌包涨) 入场=4478.59 出场=4480.59 出场时间=1758483000000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 04:00:00号 做多(涨包跌) 入场=4492.20 出场=4490.20 出场时间=1758485700000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 04:45:00号 做空(跌包涨) 入场=4477.81 出场=4447.81 出场时间=1758492900000 差价=30.00 盈利=67.00 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 05:30:00号 做空(跌包涨) 入场=4465.73 出场=4467.73 出场时间=1758491100000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 05:45:00号 做多(涨包跌) 入场=4474.17 出场=4472.17 出场时间=1758492000000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 06:45:00号 做多(涨包跌) 入场=4464.29 出场=4462.29 出场时间=1758495600000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 08:00:00号 做空(跌包涨) 入场=4443.01 出场=4445.01 出场时间=1758500100000 差价=-2.00 盈利=-4.50 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 08:30:00号 做空(跌包涨) 入场=4425.29 出场=4395.29 出场时间=1758501900000 差价=30.00 盈利=67.79 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 09:45:00号 做多(涨包跌) 入场=4343.62 出场=4341.62 出场时间=1758506400000 差价=-2.00 盈利=-4.60 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 10:00:00号 做空(跌包涨) 入场=4325.73 出场=4295.73 出场时间=1758507300000 差价=30.00 盈利=69.35 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 10:30:00号 做空(跌包涨) 入场=4307.52 出场=4277.52 出场时间=1758519000000 差价=30.00 盈利=69.65 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 11:15:00号 做多(涨包跌) 入场=4296.63 出场=4294.63 出场时间=1758511800000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 11:45:00号 做空(跌包涨) 入场=4292.42 出场=4294.42 出场时间=1758513600000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 12:00:00号 做多(涨包跌) 入场=4303.62 出场=4301.62 出场时间=1758514500000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-22 13:45:00号 做空(跌包涨) 入场=4277.81 出场=4247.81 出场时间=1758520800000 差价=30.00 盈利=70.13 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-22 16:00:00号 做空(跌包涨) 入场=4191.65 出场=4193.65 出场时间=1758528900000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-22 16:45:00号 做多(涨包跌) 入场=4198.38 出场=4196.38 出场时间=1758531600000 差价=-2.00 盈利=-4.76 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-22 18:45:00号 做空(跌包涨) 入场=4156.51 出场=4158.51 出场时间=1758538800000 差价=-2.00 盈利=-4.81 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-22 19:00:00号 做多(涨包跌) 入场=4179.99 出场=4177.99 出场时间=1758539700000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-22 19:45:00号 做多(涨包跌) 入场=4180.81 出场=4178.81 出场时间=1758542400000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-22 20:30:00号 做多(涨包跌) 入场=4181.83 出场=4179.83 出场时间=1758545100000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-22 21:00:00号 做多(涨包跌) 入场=4190.34 出场=4188.34 出场时间=1758547800000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-22 22:00:00号 做多(涨包跌) 入场=4205.46 出场=4203.46 出场时间=1758550500000 差价=-2.00 盈利=-4.76 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-22 22:30:00号 做多(涨包跌) 入场=4214.47 出场=4212.47 出场时间=1758552300000 差价=-2.00 盈利=-4.75 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-22 23:45:00号 做空(跌包涨) 入场=4168.92 出场=4170.92 出场时间=1758556800000 差价=-2.00 盈利=-4.80 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-23 00:15:00号 做多(涨包跌) 入场=4177.10 出场=4175.10 出场时间=1758558600000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-23 01:30:00号 做多(涨包跌) 入场=4165.93 出场=4163.93 出场时间=1758563100000 差价=-2.00 盈利=-4.80 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-23 02:00:00号 做空(跌包涨) 入场=4162.98 出场=4164.98 出场时间=1758564900000 差价=-2.00 盈利=-4.80 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-23 02:15:00号 做多(涨包跌) 入场=4171.60 出场=4169.60 出场时间=1758565800000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 04:15:00号 做多(涨包跌) 入场=4149.68 出场=4179.68 出场时间=1758573900000 差价=30.00 盈利=72.29 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 05:30:00号 做多(涨包跌) 入场=4186.81 出场=4184.81 出场时间=1758578400000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 06:15:00号 做空(跌包涨) 入场=4189.66 出场=4191.66 出场时间=1758580200000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 07:15:00号 做多(涨包跌) 入场=4195.96 出场=4193.96 出场时间=1758584700000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 08:00:00号 做空(跌包涨) 入场=4196.86 出场=4198.86 出场时间=1758586500000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 08:15:00号 做多(涨包跌) 入场=4203.24 出场=4201.24 出场时间=1758587400000 差价=-2.00 盈利=-4.76 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 08:45:00号 做空(跌包涨) 入场=4193.50 出场=4195.50 出场时间=1758590100000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 09:30:00号 做多(涨包跌) 入场=4195.96 出场=4193.96 出场时间=1758591900000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 09:45:00号 做空(跌包涨) 入场=4171.89 出场=4173.89 出场时间=1758592800000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 10:15:00号 做多(涨包跌) 入场=4175.71 出场=4173.71 出场时间=1758594600000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 10:30:00号 做空(跌包涨) 入场=4157.11 出场=4159.11 出场时间=1758595500000 差价=-2.00 盈利=-4.81 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 11:00:00号 做空(跌包涨) 入场=4141.99 出场=4143.99 出场时间=1758597300000 差价=-2.00 盈利=-4.83 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 11:30:00号 做多(涨包跌) 入场=4175.25 出场=4173.25 出场时间=1758600900000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 12:15:00号 做空(跌包涨) 入场=4175.15 出场=4177.15 出场时间=1758602700000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 14:00:00号 做空(跌包涨) 入场=4181.07 出场=4183.07 出场时间=1758608100000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 14:15:00号 做多(涨包跌) 入场=4192.78 出场=4222.78 出场时间=1758618000000 差价=30.00 盈利=71.55 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 15:30:00号 做空(跌包涨) 入场=4199.93 出场=4201.93 出场时间=1758613500000 差价=-2.00 盈利=-4.76 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 15:45:00号 做多(涨包跌) 入场=4205.71 出场=4203.71 出场时间=1758614400000 差价=-2.00 盈利=-4.76 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 17:00:00号 做空(跌包涨) 入场=4206.84 出场=4208.84 出场时间=1758618900000 差价=-2.00 盈利=-4.75 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 17:15:00号 做多(涨包跌) 入场=4219.98 出场=4217.98 出场时间=1758619800000 差价=-2.00 盈利=-4.74 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 17:30:00号 做空(跌包涨) 入场=4202.54 出场=4204.54 出场时间=1758634200000 差价=-2.00 盈利=-4.76 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 18:30:00号 做空(跌包涨) 入场=4188.92 出场=4190.92 出场时间=1758624300000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 19:00:00号 做多(涨包跌) 入场=4191.02 出场=4189.02 出场时间=1758626100000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 19:15:00号 做空(跌包涨) 入场=4182.13 出场=4184.13 出场时间=1758627000000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 20:15:00号 做多(涨包跌) 入场=4193.35 出场=4191.35 出场时间=1758630600000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-23 20:30:00号 做空(跌包涨) 入场=4184.09 出场=4186.09 出场时间=1758631500000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-23 20:45:00号 做多(涨包跌) 入场=4196.15 出场=4194.15 出场时间=1758632400000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-23 21:30:00号 做多(涨包跌) 入场=4194.30 出场=4192.30 出场时间=1758635100000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-23 21:45:00号 做空(跌包涨) 入场=4186.77 出场=4188.77 出场时间=1758636000000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-23 22:15:00号 做多(涨包跌) 入场=4196.04 出场=4194.04 出场时间=1758637800000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-23 22:30:00号 做空(跌包涨) 入场=4182.60 出场=4184.60 出场时间=1758638700000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-23 23:00:00号 做空(跌包涨) 入场=4172.55 出场=4174.55 出场时间=1758640500000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-23 23:45:00号 做空(跌包涨) 入场=4178.60 出场=4180.60 出场时间=1758643200000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-24 00:15:00号 做空(跌包涨) 入场=4172.91 出场=4174.91 出场时间=1758645000000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-24 00:30:00号 做多(涨包跌) 入场=4186.91 出场=4184.91 出场时间=1758645900000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-24 00:45:00号 做空(跌包涨) 入场=4170.16 出场=4172.16 出场时间=1758646800000 差价=-2.00 盈利=-4.80 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-24 01:00:00号 做多(涨包跌) 入场=4190.21 出场=4188.21 出场时间=1758647700000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-24 02:45:00号 做空(跌包涨) 入场=4149.15 出场=4151.15 出场时间=1758654000000 差价=-2.00 盈利=-4.82 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-24 03:45:00号 做空(跌包涨) 入场=4155.56 出场=4157.56 出场时间=1758657600000 差价=-2.00 盈利=-4.81 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-24 04:15:00号 做多(涨包跌) 入场=4157.54 出场=4155.54 出场时间=1758659400000 差价=-2.00 盈利=-4.81 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 04:30:00号 做空(跌包涨) 入场=4149.61 出场=4151.61 出场时间=1758660300000 差价=-2.00 盈利=-4.82 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 04:45:00号 做多(涨包跌) 入场=4163.34 出场=4161.34 出场时间=1758669300000 差价=-2.00 盈利=-4.80 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 06:45:00号 做多(涨包跌) 入场=4182.21 出场=4180.21 出场时间=1758668400000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 09:15:00号 做多(涨包跌) 入场=4182.12 出场=4180.12 出场时间=1758679200000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 10:15:00号 做空(跌包涨) 入场=4168.31 出场=4170.31 出场时间=1758681000000 差价=-2.00 盈利=-4.80 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 12:15:00号 做空(跌包涨) 入场=4098.12 出场=4100.12 出场时间=1758688200000 差价=-2.00 盈利=-4.88 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 16:15:00号 做多(涨包跌) 入场=4174.81 出场=4172.81 出场时间=1758702600000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 16:30:00号 做空(跌包涨) 入场=4168.12 出场=4170.12 出场时间=1758703500000 差价=-2.00 盈利=-4.80 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 16:45:00号 做多(涨包跌) 入场=4175.13 出场=4173.13 出场时间=1758704400000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 17:30:00号 做多(涨包跌) 入场=4174.40 出场=4172.40 出场时间=1758708900000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 18:15:00号 做空(跌包涨) 入场=4177.63 出场=4179.63 出场时间=1758711600000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 20:45:00号 做多(涨包跌) 入场=4181.50 出场=4179.50 出场时间=1758718800000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-25 00:30:00号 做多(涨包跌) 入场=4167.86 出场=4165.86 出场时间=1758733200000 差价=-2.00 盈利=-4.80 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-25 01:15:00号 做多(涨包跌) 入场=4178.77 出场=4176.77 出场时间=1758735000000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-25 01:45:00号 做多(涨包跌) 入场=4186.01 出场=4184.01 出场时间=1758736800000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 07:00:00号 做空(跌包涨) 入场=4151.98 出场=4153.98 出场时间=1758755700000 差价=-2.00 盈利=-4.82 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 07:15:00号 做多(涨包跌) 入场=4158.00 出场=4156.00 出场时间=1758756600000 差价=-2.00 盈利=-4.81 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 08:30:00号 做空(跌包涨) 入场=4137.01 出场=4139.01 出场时间=1758761100000 差价=-2.00 盈利=-4.83 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 09:00:00号 做空(跌包涨) 入场=4126.96 出场=4096.96 出场时间=1758762900000 差价=30.00 盈利=72.69 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 10:45:00号 做多(涨包跌) 入场=4099.28 出场=4097.28 出场时间=1758769200000 差价=-2.00 盈利=-4.88 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 11:15:00号 做空(跌包涨) 入场=4086.56 出场=4056.56 出场时间=1758771000000 差价=30.00 盈利=73.41 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 12:15:00号 做空(跌包涨) 入场=4045.83 出场=4015.83 出场时间=1758774600000 差价=30.00 盈利=74.15 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 13:00:00号 做空(跌包涨) 入场=4000.17 出场=4002.17 出场时间=1758777300000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 16:00:00号 做空(跌包涨) 入场=4006.93 出场=4008.93 出场时间=1758788100000 差价=-2.00 盈利=-4.99 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 16:15:00号 做多(涨包跌) 入场=4029.59 出场=4027.59 出场时间=1758789000000 差价=-2.00 盈利=-4.96 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 16:45:00号 做空(跌包涨) 入场=4010.29 出场=4012.29 出场时间=1758790800000 差价=-2.00 盈利=-4.99 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 19:45:00号 做空(跌包涨) 入场=3998.02 出场=4000.02 出场时间=1758801600000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 20:30:00号 做空(跌包涨) 入场=3939.54 出场=3941.54 出场时间=1758804300000 差价=-2.00 盈利=-5.08 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 21:30:00号 做多(涨包跌) 入场=4001.35 出场=3999.35 出场时间=1758807900000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 21:45:00号 做空(跌包涨) 入场=3957.31 出场=3959.31 出场时间=1758808800000 差价=-2.00 盈利=-5.05 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-26 00:15:00号 做空(跌包涨) 入场=3996.68 出场=3966.68 出场时间=1758817800000 差价=30.00 盈利=75.06 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-26 01:45:00号 做空(跌包涨) 入场=3865.17 出场=3835.17 出场时间=1758823200000 差价=30.00 盈利=77.62 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-26 03:30:00号 做多(涨包跌) 入场=3937.43 出场=3935.43 出场时间=1758829500000 差价=-2.00 盈利=-5.08 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 04:45:00号 做空(跌包涨) 入场=3891.53 出场=3893.53 出场时间=1758834000000 差价=-2.00 盈利=-5.14 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 05:15:00号 做多(涨包跌) 入场=3898.34 出场=3896.34 出场时间=1758835800000 差价=-2.00 盈利=-5.13 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 06:00:00号 做多(涨包跌) 入场=3903.57 出场=3933.57 出场时间=1758838500000 差价=30.00 盈利=76.85 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 08:45:00号 做空(跌包涨) 入场=3872.95 出场=3874.95 出场时间=1758848400000 差价=-2.00 盈利=-5.16 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 09:00:00号 做多(涨包跌) 入场=3930.76 出场=3928.76 出场时间=1758849300000 差价=-2.00 盈利=-5.09 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 09:30:00号 做空(跌包涨) 入场=3927.77 出场=3929.77 出场时间=1758851100000 差价=-2.00 盈利=-5.09 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 09:45:00号 做多(涨包跌) 入场=3941.60 出场=3971.60 出场时间=1758860100000 差价=30.00 盈利=76.11 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 10:45:00号 做多(涨包跌) 入场=3957.81 出场=3955.81 出场时间=1758855600000 差价=-2.00 盈利=-5.05 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 11:15:00号 做多(涨包跌) 入场=3959.57 出场=3957.57 出场时间=1758857400000 差价=-2.00 盈利=-5.05 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 11:30:00号 做空(跌包涨) 入场=3954.21 出场=3956.21 出场时间=1758858300000 差价=-2.00 盈利=-5.06 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 12:30:00号 做空(跌包涨) 入场=3960.89 出场=3930.89 出场时间=1758865500000 差价=30.00 盈利=75.74 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 13:45:00号 做空(跌包涨) 入场=3942.77 出场=3912.77 出场时间=1758866400000 差价=30.00 盈利=76.09 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 15:00:00号 做空(跌包涨) 入场=3919.00 出场=3921.00 出场时间=1758870900000 差价=-2.00 盈利=-5.10 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 16:00:00号 做多(涨包跌) 入场=3936.60 出场=3934.60 出场时间=1758874500000 差价=-2.00 盈利=-5.08 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 16:45:00号 做多(涨包跌) 入场=3937.94 出场=3935.94 出场时间=1758877200000 差价=-2.00 盈利=-5.08 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-26 17:00:00号 做空(跌包涨) 入场=3932.27 出场=3934.27 出场时间=1758878100000 差价=-2.00 盈利=-5.09 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-26 17:30:00号 做空(跌包涨) 入场=3925.74 出场=3895.74 出场时间=1758880800000 差价=30.00 盈利=76.42 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-26 18:15:00号 做空(跌包涨) 入场=3885.27 出场=3887.27 出场时间=1758882600000 差价=-2.00 盈利=-5.15 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-26 18:45:00号 做多(涨包跌) 入场=3886.20 出场=3884.20 出场时间=1758884400000 差价=-2.00 盈利=-5.15 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-26 19:15:00号 做空(跌包涨) 入场=3875.50 出场=3877.50 出场时间=1758886200000 差价=-2.00 盈利=-5.16 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-26 19:30:00号 做多(涨包跌) 入场=3888.98 出场=3886.98 出场时间=1758888000000 差价=-2.00 盈利=-5.14 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-26 21:45:00号 做多(涨包跌) 入场=3937.17 出场=3967.17 出场时间=1758895200000 差价=30.00 盈利=76.20 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-26 23:00:00号 做空(跌包涨) 入场=3929.62 出场=3931.62 出场时间=1758899700000 差价=-2.00 盈利=-5.09 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-27 00:15:00号 做多(涨包跌) 入场=3962.86 出场=3992.86 出场时间=1758906000000 差价=30.00 盈利=75.70 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-27 02:45:00号 做空(跌包涨) 入场=4040.09 出场=4042.09 出场时间=1758913200000 差价=-2.00 盈利=-4.95 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-27 03:15:00号 做空(跌包涨) 入场=4036.73 出场=4006.73 出场时间=1758915000000 差价=30.00 盈利=74.32 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-27 04:00:00号 做多(涨包跌) 入场=4021.39 出场=4019.39 出场时间=1758917700000 差价=-2.00 盈利=-4.97 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-27 04:30:00号 做空(跌包涨) 入场=4016.00 出场=4018.00 出场时间=1758922200000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-27 05:15:00号 做多(涨包跌) 入场=4014.71 出场=4012.71 出场时间=1758922200000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-27 05:45:00号 做空(跌包涨) 入场=4010.24 出场=4012.24 出场时间=1758924000000 差价=-2.00 盈利=-4.99 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 06:00:00号 做多(涨包跌) 入场=4021.35 出场=4019.35 出场时间=1758927600000 差价=-2.00 盈利=-4.97 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 07:00:00号 做空(跌包涨) 入场=4024.56 出场=4026.56 出场时间=1758929400000 差价=-2.00 盈利=-4.97 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 08:45:00号 做空(跌包涨) 入场=4020.15 出场=4022.15 出场时间=1758934800000 差价=-2.00 盈利=-4.97 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 10:00:00号 做多(涨包跌) 入场=4016.68 出场=4014.68 出场时间=1758939300000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 10:30:00号 做空(跌包涨) 入场=4011.24 出场=4013.24 出场时间=1758941100000 差价=-2.00 盈利=-4.99 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 11:00:00号 做空(跌包涨) 入场=4010.82 出场=4012.82 出场时间=1758942900000 差价=-2.00 盈利=-4.99 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 11:30:00号 做多(涨包跌) 入场=4021.64 出场=4019.64 出场时间=1758944700000 差价=-2.00 盈利=-4.97 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 12:00:00号 做空(跌包涨) 入场=4017.19 出场=4019.19 出场时间=1758946500000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 12:30:00号 做多(涨包跌) 入场=4024.64 出场=4022.64 出场时间=1758948300000 差价=-2.00 盈利=-4.97 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 13:45:00号 做多(涨包跌) 入场=4017.61 出场=4015.61 出场时间=1758952800000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 14:15:00号 做空(跌包涨) 入场=4013.01 出场=4015.01 出场时间=1758954600000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 14:45:00号 做多(涨包跌) 入场=4014.09 出场=4012.09 出场时间=1758957300000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 15:30:00号 做空(跌包涨) 入场=4011.76 出场=3981.76 出场时间=1758962700000 差价=30.00 盈利=74.78 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 18:45:00号 做多(涨包跌) 入场=4011.07 出场=4009.07 出场时间=1758970800000 差价=-2.00 盈利=-4.99 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 19:30:00号 做多(涨包跌) 入场=4011.08 出场=4009.08 出场时间=1758973500000 差价=-2.00 盈利=-4.99 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-27 19:45:00号 做空(跌包涨) 入场=4003.03 出场=4005.03 出场时间=1758976200000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-27 22:00:00号 做空(跌包涨) 入场=4018.39 出场=4020.39 出场时间=1758982500000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-27 22:30:00号 做空(跌包涨) 入场=4017.14 出场=4019.14 出场时间=1758987900000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 00:15:00号 做空(跌包涨) 入场=4010.03 出场=4012.03 出场时间=1758990600000 差价=-2.00 盈利=-4.99 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 01:45:00号 做多(涨包跌) 入场=3994.29 出场=3992.29 出场时间=1758996000000 差价=-2.00 盈利=-5.01 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 03:00:00号 做多(涨包跌) 入场=3994.64 出场=3992.64 出场时间=1759000500000 差价=-2.00 盈利=-5.01 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 03:30:00号 做多(涨包跌) 入场=3997.57 出场=3995.57 出场时间=1759002300000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 04:00:00号 做多(涨包跌) 入场=4000.64 出场=4030.64 出场时间=1759011300000 差价=30.00 盈利=74.99 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 05:45:00号 做多(涨包跌) 入场=4014.84 出场=4012.84 出场时间=1759010400000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 06:15:00号 做多(涨包跌) 入场=4021.22 出场=4019.22 出场时间=1759012200000 差价=-2.00 盈利=-4.97 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 06:45:00号 做空(跌包涨) 入场=4017.93 出场=4019.93 出场时间=1759014000000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 07:30:00号 做空(跌包涨) 入场=4016.91 出场=4018.91 出场时间=1759016700000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 08:00:00号 做多(涨包跌) 入场=4016.99 出场=4014.99 出场时间=1759018500000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 08:15:00号 做空(跌包涨) 入场=4011.14 出场=4013.14 出场时间=1759019400000 差价=-2.00 盈利=-4.99 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 08:30:00号 做多(涨包跌) 入场=4027.69 出场=4025.69 出场时间=1759020300000 差价=-2.00 盈利=-4.97 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 09:45:00号 做空(跌包涨) 入场=3995.87 出场=3997.87 出场时间=1759024800000 差价=-2.00 盈利=-5.01 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 10:30:00号 做多(涨包跌) 入场=4002.88 出场=4000.88 出场时间=1759027500000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 11:15:00号 做多(涨包跌) 入场=4003.08 出场=4001.08 出场时间=1759030200000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 12:15:00号 做空(跌包涨) 入场=3996.98 出场=3998.98 出场时间=1759033800000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 12:45:00号 做多(涨包跌) 入场=4002.57 出场=4000.57 出场时间=1759040100000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 13:15:00号 做空(跌包涨) 入场=4001.69 出场=4003.69 出场时间=1759037400000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 13:30:00号 做多(涨包跌) 入场=4009.87 出场=4007.87 出场时间=1759038300000 差价=-2.00 盈利=-4.99 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 14:00:00号 做空(跌包涨) 入场=4008.99 出场=4010.99 出场时间=1759040100000 差价=-2.00 盈利=-4.99 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 14:30:00号 做空(跌包涨) 入场=3998.62 出场=4000.62 出场时间=1759041900000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 16:15:00号 做空(跌包涨) 入场=4010.49 出场=3980.49 出场时间=1759062600000 差价=30.00 盈利=74.80 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 18:00:00号 做空(跌包涨) 入场=3993.77 出场=3995.77 出场时间=1759055400000 差价=-2.00 盈利=-5.01 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 18:45:00号 做多(涨包跌) 入场=3996.14 出场=3994.14 出场时间=1759057200000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 20:45:00号 做空(跌包涨) 入场=3969.72 出场=3971.72 出场时间=1759064400000 差价=-2.00 盈利=-5.04 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 23:00:00号 做多(涨包跌) 入场=4022.99 出场=4020.99 出场时间=1759072500000 差价=-2.00 盈利=-4.97 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 23:45:00号 做空(跌包涨) 入场=4019.98 出场=4021.98 出场时间=1759075200000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-29 00:15:00号 做多(涨包跌) 入场=4032.80 出场=4030.80 出场时间=1759077000000 差价=-2.00 盈利=-4.96 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-29 01:00:00号 做多(涨包跌) 入场=4031.84 出场=4029.84 出场时间=1759079700000 差价=-2.00 盈利=-4.96 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.417 | INFO | __main__:<module>:216 - 2025-09-29 01:45:00号 做多(涨包跌) 入场=4043.55 出场=4041.55 出场时间=1759082400000 差价=-2.00 盈利=-4.95 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.417 | INFO | __main__:<module>:216 - 2025-09-29 02:45:00号 做多(涨包跌) 入场=4038.12 出场=4036.12 出场时间=1759086000000 差价=-2.00 盈利=-4.95 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.417 | INFO | __main__:<module>:216 - 2025-09-29 03:15:00号 做空(跌包涨) 入场=4032.23 出场=4034.23 出场时间=1759087800000 差价=-2.00 盈利=-4.96 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.417 | INFO | __main__:<module>:216 - 2025-09-29 04:00:00号 做多(涨包跌) 入场=4037.42 出场=4035.42 出场时间=1759090500000 差价=-2.00 盈利=-4.95 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.417 | INFO | __main__:<module>:216 - 2025-09-29 04:30:00号 做多(涨包跌) 入场=4046.49 出场=4044.49 出场时间=1759095000000 差价=-2.00 盈利=-4.94 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.417 | INFO | __main__:<module>:216 - 2025-09-29 05:30:00号 做空(跌包涨) 入场=4048.44 出场=4050.44 出场时间=1759096800000 差价=-2.00 盈利=-4.94 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.417 | INFO | __main__:<module>:216 - 2025-09-29 07:45:00号 做多(涨包跌) 入场=4137.26 出场=4135.26 出场时间=1759104000000 差价=-2.00 盈利=-4.83 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.417 | INFO | __main__:<module>:216 - 2025-09-29 08:15:00号 做空(跌包涨) 入场=4134.85 出场=4136.85 出场时间=1759105800000 差价=-2.00 盈利=-4.84 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.417 | INFO | __main__:<module>:216 - 2025-09-29 08:45:00号 做空(跌包涨) 入场=4126.53 出场=4128.53 出场时间=1759107600000 差价=-2.00 盈利=-4.85 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.417 | INFO | __main__:<module>:216 - 2025-09-29 09:15:00号 做多(涨包跌) 入场=4131.72 出场=4129.72 出场时间=1759109400000 差价=-2.00 盈利=-4.84 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.418 | INFO | __main__:<module>:216 - 2025-09-29 09:30:00号 做空(跌包涨) 入场=4117.93 出场=4119.93 出场时间=1759110300000 差价=-2.00 盈利=-4.86 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.418 | INFO | __main__:<module>:216 - 2025-09-29 10:00:00号 做空(跌包涨) 入场=4112.65 出场=4114.65 出场时间=1759112100000 差价=-2.00 盈利=-4.86 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.418 | INFO | __main__:<module>:216 - 2025-09-29 10:45:00号 做空(跌包涨) 入场=4107.29 出场=4109.29 出场时间=1759114800000 差价=-2.00 盈利=-4.87 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.418 | INFO | __main__:<module>:216 - 2025-09-29 11:15:00号 做空(跌包涨) 入场=4102.41 出场=4104.41 出场时间=1759116600000 差价=-2.00 盈利=-4.88 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.418 | INFO | __main__:<module>:216 - 2025-09-29 12:00:00号 做多(涨包跌) 入场=4119.67 出场=4117.67 出场时间=1759119300000 差价=-2.00 盈利=-4.85 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.418 | INFO | __main__:<module>:216 - 2025-09-29 12:45:00号 做多(涨包跌) 入场=4118.75 出场=4116.75 出场时间=1759122000000 差价=-2.00 盈利=-4.86 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.418 | INFO | __main__:<module>:216 - 2025-09-29 13:45:00号 做多(涨包跌) 入场=4109.88 出场=4107.88 出场时间=1759125600000 差价=-2.00 盈利=-4.87 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-29 14:15:00号 做空(跌包涨) 入场=4097.98 出场=4099.98 出场时间=1759127400000 差价=-2.00 盈利=-4.88 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-29 15:00:00号 做空(跌包涨) 入场=4095.36 出场=4097.36 出场时间=1759130100000 差价=-2.00 盈利=-4.88 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-29 15:30:00号 做空(跌包涨) 入场=4092.53 出场=4094.53 出场时间=1759131900000 差价=-2.00 盈利=-4.89 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-29 15:45:00号 做多(涨包跌) 入场=4104.99 出场=4134.99 出场时间=1759137300000 差价=30.00 盈利=73.08 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-29 20:00:00号 做空(跌包涨) 入场=4100.87 出场=4102.87 出场时间=1759148100000 差价=-2.00 盈利=-4.88 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-29 20:15:00号 做多(涨包跌) 入场=4108.92 出场=4106.92 出场时间=1759149000000 差价=-2.00 盈利=-4.87 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-29 20:45:00号 做空(跌包涨) 入场=4102.06 出场=4104.06 出场时间=1759150800000 差价=-2.00 盈利=-4.88 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-29 21:15:00号 做多(涨包跌) 入场=4102.99 出场=4132.99 出场时间=1759152600000 差价=30.00 盈利=73.12 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-29 22:45:00号 做多(涨包跌) 入场=4189.88 出场=4187.88 出场时间=1759158000000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-30 00:30:00号 做空(跌包涨) 入场=4156.52 出场=4126.52 出场时间=1759165200000 差价=30.00 盈利=72.18 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-30 01:30:00号 做多(涨包跌) 入场=4145.04 出场=4143.04 出场时间=1759168800000 差价=-2.00 盈利=-4.83 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-30 02:00:00号 做多(涨包跌) 入场=4153.13 出场=4151.13 出场时间=1759169700000 差价=-2.00 盈利=-4.82 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-30 03:45:00号 做多(涨包跌) 入场=4180.31 出场=4210.31 出场时间=1759177800000 差价=30.00 盈利=71.77 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-30 04:45:00号 做多(涨包跌) 入场=4229.13 出场=4227.13 出场时间=1759179600000 差价=-2.00 盈利=-4.73 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-30 07:00:00号 做多(涨包跌) 入场=4224.09 出场=4222.09 出场时间=1759187700000 差价=-2.00 盈利=-4.73 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-30 08:45:00号 做多(涨包跌) 入场=4216.80 出场=4214.80 出场时间=1759194000000 差价=-2.00 盈利=-4.74 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 10:15:00号 做空(跌包涨) 入场=4198.83 出场=4200.83 出场时间=1759199400000 差价=-2.00 盈利=-4.76 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 10:45:00号 做多(涨包跌) 入场=4201.68 出场=4199.68 出场时间=1759201200000 差价=-2.00 盈利=-4.76 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 11:30:00号 做多(涨包跌) 入场=4199.59 出场=4197.59 出场时间=1759203900000 差价=-2.00 盈利=-4.76 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 12:00:00号 做空(跌包涨) 入场=4196.16 出场=4198.16 出场时间=1759205700000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 14:00:00号 做多(涨包跌) 入场=4182.43 出场=4180.43 出场时间=1759217400000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 15:00:00号 做空(跌包涨) 入场=4188.05 出场=4190.05 出场时间=1759216500000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 17:30:00号 做多(涨包跌) 入场=4168.78 出场=4166.78 出场时间=1759225500000 差价=-2.00 盈利=-4.80 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 19:15:00号 做空(跌包涨) 入场=4153.02 出场=4155.02 出场时间=1759231800000 差价=-2.00 盈利=-4.82 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 19:30:00号 做多(涨包跌) 入场=4158.87 出场=4156.87 出场时间=1759233600000 差价=-2.00 盈利=-4.81 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 20:30:00号 做多(涨包跌) 入场=4165.40 出场=4163.40 出场时间=1759236300000 差价=-2.00 盈利=-4.80 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 21:00:00号 做空(跌包涨) 入场=4156.14 出场=4158.14 出场时间=1759238100000 差价=-2.00 盈利=-4.81 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 21:30:00号 做多(涨包跌) 入场=4164.44 出场=4162.44 出场时间=1759239900000 差价=-2.00 盈利=-4.80 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 22:00:00号 做空(跌包涨) 入场=4152.29 出场=4122.29 出场时间=1759241700000 差价=30.00 盈利=72.25 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 23:15:00号 做空(跌包涨) 入场=4115.98 出场=4117.98 出场时间=1759247100000 差价=-2.00 盈利=-4.86 开仓手续费=5u 平仓手续费=5.00
|
||||
""" # ← 这里粘贴你的完整日志文本
|
||||
|
||||
pattern = re.compile(
|
||||
r"(?P<time>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}).*?(?P<direction>做多|做空).*?入场=(?P<entry>[\d.]+).*?出场=(?P<exit>[\d.]+).*?盈利=(?P<profit>[-\d.]+)"
|
||||
)
|
||||
|
||||
trades = []
|
||||
for match in pattern.finditer(log_data):
|
||||
t = match.groupdict()
|
||||
t["entry"] = float(t["entry"])
|
||||
t["exit"] = float(t["exit"])
|
||||
t["profit"] = float(t["profit"])
|
||||
t["time"] = datetime.strptime(t["time"], "%Y-%m-%d %H:%M:%S")
|
||||
trades.append(t)
|
||||
|
||||
# 按时间排序
|
||||
trades.sort(key=lambda x: x["time"])
|
||||
|
||||
# 模式A:禁止重叠开仓
|
||||
no_overlap_trades = []
|
||||
last_close_time = None
|
||||
for t in trades:
|
||||
if last_close_time and t["time"] < last_close_time:
|
||||
continue # 忽略重叠交易
|
||||
no_overlap_trades.append(t)
|
||||
last_close_time = t["time"] # 这里假设出场时间等于该记录时间
|
||||
|
||||
# 模式B:信号来了就开单(全部计算)
|
||||
all_trades = trades
|
||||
|
||||
# 汇总
|
||||
def summary(name, trade_list):
|
||||
total = sum(t["profit"] for t in trade_list)
|
||||
print(f"\n{name}:")
|
||||
print(f"交易次数:{len(trade_list)}")
|
||||
print(f"总盈利:{total:.2f} USDT")
|
||||
if trade_list:
|
||||
print(f"平均每单:{total / len(trade_list):.2f} USDT")
|
||||
|
||||
summary("模式A(不允许重叠)", no_overlap_trades)
|
||||
summary("模式B(信号即开)", all_trades)
|
||||
@@ -1,83 +0,0 @@
|
||||
import random
|
||||
import time
|
||||
|
||||
from curl_cffi import requests
|
||||
|
||||
from models.weex import Weex1
|
||||
|
||||
headers = {
|
||||
'accept': 'application/json, text/plain, */*',
|
||||
'accept-language': 'zh-CN,zh;q=0.9,zh-HK;q=0.8,en;q=0.7',
|
||||
'appversion': '2.0.0',
|
||||
'bundleid': '',
|
||||
'cache-control': 'no-cache',
|
||||
'language': 'zh_CN',
|
||||
'origin': 'https://www.weex.com',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=1, i',
|
||||
'referer': 'https://www.weex.com/',
|
||||
'sec-ch-ua': '"Google Chrome";v="141", "Not?A_Brand";v="8", "Chromium";v="141"',
|
||||
'sec-ch-ua-mobile': '?0',
|
||||
'sec-ch-ua-platform': '"Windows"',
|
||||
'sec-fetch-dest': 'empty',
|
||||
'sec-fetch-mode': 'cors',
|
||||
'sec-fetch-site': 'cross-site',
|
||||
'sidecar': '0115665ae5d5a18da1543cd0deabd52588c09f4d3b028876659f0801ca7af5a3ba',
|
||||
'terminalcode': '55a01525ddf9e8f4bb158345db69b510',
|
||||
'terminaltype': '1',
|
||||
'traceid': 'mgotla61s5segrwkkw',
|
||||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36',
|
||||
'vs': 'Z567O6188nsWfe9Njo9N7KsAbL0RO6ho',
|
||||
'x-sig': 'bb0f194c38f69c38ffbcb361c27c7295',
|
||||
'x-timestamp': '1760341017529',
|
||||
}
|
||||
|
||||
klineId = None
|
||||
klineTime = None
|
||||
contractId = None
|
||||
|
||||
while True:
|
||||
|
||||
for i in range(500):
|
||||
print(i)
|
||||
params = {
|
||||
'languageType': '1',
|
||||
'sign': 'SIGN',
|
||||
'timeZone': 'string',
|
||||
'contractId': '10000002',
|
||||
'productCode': 'ethusdt',
|
||||
'priceType': 'LAST_PRICE',
|
||||
'klineType': 'MINUTE_1',
|
||||
'limit': '200',
|
||||
}
|
||||
|
||||
if klineId:
|
||||
params['nextKey.klineId'] = klineId
|
||||
params['nextKey.contractId'] = contractId
|
||||
|
||||
if klineTime:
|
||||
params['nextKey.klineTime'] = klineTime
|
||||
|
||||
response = requests.get('https://http-gateway1.janapw.com/api/v1/public/quote/v1/getKlineV2', params=params,
|
||||
headers=headers)
|
||||
|
||||
klineId = response.json()["data"]["nextKey"]["klineId"]
|
||||
klineTime = response.json()["data"]["nextKey"]["klineTime"]
|
||||
contractId = response.json()["data"]["nextKey"]["contractId"]
|
||||
|
||||
for data in response.json()["data"]["dataList"]:
|
||||
# print(data)
|
||||
|
||||
Weex1.get_or_create(
|
||||
id=int(data[4]),
|
||||
defaults={
|
||||
'open': float(data[3]),
|
||||
'high': float(data[1]),
|
||||
'low': float(data[2]),
|
||||
'close': float(data[0]),
|
||||
}
|
||||
)
|
||||
|
||||
time.sleep(random.randint(1, 50))
|
||||
|
||||
# time.sleep(3600)
|
||||
@@ -1,83 +0,0 @@
|
||||
import time
|
||||
|
||||
from curl_cffi import requests
|
||||
|
||||
from models.weex import Weex15
|
||||
|
||||
headers = {
|
||||
'accept': 'application/json, text/plain, */*',
|
||||
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
|
||||
'appversion': '2.0.0',
|
||||
'bundleid': '',
|
||||
'cache-control': 'no-cache',
|
||||
'dnt': '1',
|
||||
'language': 'zh_CN',
|
||||
'origin': 'https://www.weeaxs.site',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=1, i',
|
||||
'referer': 'https://www.weeaxs.site/',
|
||||
'sec-ch-ua': '"Chromium";v="140", "Not=A?Brand";v="24", "Microsoft Edge";v="140"',
|
||||
'sec-ch-ua-mobile': '?0',
|
||||
'sec-ch-ua-platform': '"Windows"',
|
||||
'sec-fetch-dest': 'empty',
|
||||
'sec-fetch-mode': 'cors',
|
||||
'sec-fetch-site': 'cross-site',
|
||||
'sidecar': '01bfdfef90d2d6213c86b09f3a00d696d366752996a0b6692a33969a67fcd243df',
|
||||
'terminalcode': '89adf61538715df59eb4f6414981484e',
|
||||
'terminaltype': '1',
|
||||
'traceid': 'mgoh8eoehxp15lxnbv',
|
||||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36 Edg/140.0.0.0',
|
||||
'vs': 'G5h7sX78yNSykC9xtTmA7O2lSKqAk6Sp',
|
||||
'x-sig': '74ec6a65f3886f24a8b062e3b41edb1a',
|
||||
'x-timestamp': '1760320261454',
|
||||
}
|
||||
|
||||
klineId = None
|
||||
klineTime = None
|
||||
contractId = None
|
||||
|
||||
while True:
|
||||
|
||||
for i in range(10):
|
||||
print(i)
|
||||
params = {
|
||||
'languageType': '1',
|
||||
'sign': 'SIGN',
|
||||
'timeZone': 'string',
|
||||
'contractId': '10000002',
|
||||
'productCode': 'ethusdt',
|
||||
'priceType': 'LAST_PRICE',
|
||||
'klineType': 'MINUTE_15',
|
||||
'limit': '329',
|
||||
}
|
||||
|
||||
if klineId:
|
||||
params['nextKey.klineId'] = klineId
|
||||
params['nextKey.contractId'] = contractId
|
||||
|
||||
if klineTime:
|
||||
params['nextKey.klineTime'] = klineTime
|
||||
|
||||
response = requests.get('https://http-gateway1.janapw.com/api/v1/public/quote/v1/getKlineV2', params=params,
|
||||
headers=headers)
|
||||
|
||||
klineId = response.json()["data"]["nextKey"]["klineId"]
|
||||
klineTime = response.json()["data"]["nextKey"]["klineTime"]
|
||||
contractId = response.json()["data"]["nextKey"]["contractId"]
|
||||
|
||||
for data in response.json()["data"]["dataList"]:
|
||||
# print(data)
|
||||
|
||||
Weex15.get_or_create(
|
||||
id=int(data[4]),
|
||||
defaults={
|
||||
'open': float(data[3]),
|
||||
'high': float(data[1]),
|
||||
'low': float(data[2]),
|
||||
'close': float(data[0]),
|
||||
}
|
||||
)
|
||||
|
||||
# time.sleep(1)
|
||||
|
||||
time.sleep(3600)
|
||||
@@ -1,83 +0,0 @@
|
||||
import time
|
||||
|
||||
from curl_cffi import requests
|
||||
|
||||
from models.weex import Weex1Hour
|
||||
|
||||
headers = {
|
||||
'accept': 'application/json, text/plain, */*',
|
||||
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
|
||||
'appversion': '2.0.0',
|
||||
'bundleid': '',
|
||||
'cache-control': 'no-cache',
|
||||
'dnt': '1',
|
||||
'language': 'zh_CN',
|
||||
'origin': 'https://www.weeaxs.site',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=1, i',
|
||||
'referer': 'https://www.weeaxs.site/',
|
||||
'sec-ch-ua': '"Chromium";v="140", "Not=A?Brand";v="24", "Microsoft Edge";v="140"',
|
||||
'sec-ch-ua-mobile': '?0',
|
||||
'sec-ch-ua-platform': '"Windows"',
|
||||
'sec-fetch-dest': 'empty',
|
||||
'sec-fetch-mode': 'cors',
|
||||
'sec-fetch-site': 'cross-site',
|
||||
'sidecar': '01bfdfef90d2d6213c86b09f3a00d696d366752996a0b6692a33969a67fcd243df',
|
||||
'terminalcode': '89adf61538715df59eb4f6414981484e',
|
||||
'terminaltype': '1',
|
||||
'traceid': 'mgoh8eoehxp15lxnbv',
|
||||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36 Edg/140.0.0.0',
|
||||
'vs': 'G5h7sX78yNSykC9xtTmA7O2lSKqAk6Sp',
|
||||
'x-sig': '74ec6a65f3886f24a8b062e3b41edb1a',
|
||||
'x-timestamp': '1760320261454',
|
||||
}
|
||||
|
||||
klineId = None
|
||||
klineTime = None
|
||||
contractId = None
|
||||
|
||||
while True:
|
||||
|
||||
for i in range(10):
|
||||
print(i)
|
||||
params = {
|
||||
'languageType': '1',
|
||||
'sign': 'SIGN',
|
||||
'timeZone': 'string',
|
||||
'contractId': '10000002',
|
||||
'productCode': 'ethusdt',
|
||||
'priceType': 'LAST_PRICE',
|
||||
'klineType': 'HOUR_1',
|
||||
'limit': '570',
|
||||
}
|
||||
|
||||
if klineId:
|
||||
params['nextKey.klineId'] = klineId
|
||||
params['nextKey.contractId'] = contractId
|
||||
|
||||
if klineTime:
|
||||
params['nextKey.klineTime'] = klineTime
|
||||
|
||||
response = requests.get('https://http-gateway1.janapw.com/api/v1/public/quote/v1/getKlineV2', params=params,
|
||||
headers=headers)
|
||||
|
||||
klineId = response.json()["data"]["nextKey"]["klineId"]
|
||||
klineTime = response.json()["data"]["nextKey"]["klineTime"]
|
||||
contractId = response.json()["data"]["nextKey"]["contractId"]
|
||||
|
||||
for data in response.json()["data"]["dataList"]:
|
||||
# print(data)
|
||||
|
||||
Weex1Hour.get_or_create(
|
||||
id=int(data[4]),
|
||||
defaults={
|
||||
'open': float(data[3]),
|
||||
'high': float(data[1]),
|
||||
'low': float(data[2]),
|
||||
'close': float(data[0]),
|
||||
}
|
||||
)
|
||||
|
||||
# time.sleep(1)
|
||||
|
||||
time.sleep(3600)
|
||||
866
weex/最大回撤计算.py
866
weex/最大回撤计算.py
@@ -1,866 +0,0 @@
|
||||
import re
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
def calculate_max_drawdown(log_data):
|
||||
"""
|
||||
计算交易日志的最大回撤
|
||||
"""
|
||||
# 解析日志数据,提取每笔交易的盈利
|
||||
profits = []
|
||||
|
||||
for line in log_data.split('\n'):
|
||||
if not line.strip():
|
||||
continue
|
||||
|
||||
# 使用正则表达式提取盈利数值
|
||||
match = re.search(r'盈利=([-\d.]+)', line)
|
||||
if match:
|
||||
profit = float(match.group(1))
|
||||
profits.append(profit)
|
||||
|
||||
if not profits:
|
||||
return 0, 0, 0, 0, []
|
||||
|
||||
# 计算累计收益
|
||||
cumulative_returns = []
|
||||
cumulative = 0
|
||||
for profit in profits:
|
||||
cumulative += profit
|
||||
cumulative_returns.append(cumulative)
|
||||
|
||||
# 计算最大回撤
|
||||
max_drawdown = 0
|
||||
max_drawdown_start = 0
|
||||
max_drawdown_end = 0
|
||||
peak = cumulative_returns[0]
|
||||
|
||||
for i in range(1, len(cumulative_returns)):
|
||||
if cumulative_returns[i] > peak:
|
||||
peak = cumulative_returns[i]
|
||||
else:
|
||||
drawdown = peak - cumulative_returns[i]
|
||||
if drawdown > max_drawdown:
|
||||
max_drawdown = drawdown
|
||||
max_drawdown_start = cumulative_returns.index(peak)
|
||||
max_drawdown_end = i
|
||||
|
||||
# 计算其他统计指标
|
||||
total_trades = len(profits)
|
||||
winning_trades = len([p for p in profits if p > 0])
|
||||
losing_trades = len([p for p in profits if p < 0])
|
||||
win_rate = winning_trades / total_trades if total_trades > 0 else 0
|
||||
total_profit = sum(profits)
|
||||
|
||||
return {
|
||||
'max_drawdown': max_drawdown,
|
||||
'max_drawdown_percentage': (max_drawdown / peak * 100) if peak != 0 else 0,
|
||||
'max_drawdown_period': (max_drawdown_start, max_drawdown_end),
|
||||
'total_trades': total_trades,
|
||||
'winning_trades': winning_trades,
|
||||
'losing_trades': losing_trades,
|
||||
'win_rate': win_rate,
|
||||
'total_profit': total_profit,
|
||||
'average_profit': total_profit / total_trades if total_trades > 0 else 0,
|
||||
'cumulative_returns': cumulative_returns,
|
||||
'individual_profits': profits
|
||||
}
|
||||
|
||||
|
||||
def print_analysis_results(results):
|
||||
"""打印分析结果"""
|
||||
print("=" * 60)
|
||||
print("交易策略分析报告")
|
||||
print("=" * 60)
|
||||
|
||||
print(f"总交易次数: {results['total_trades']}")
|
||||
print(f"盈利交易: {results['winning_trades']} 次")
|
||||
print(f"亏损交易: {results['losing_trades']} 次")
|
||||
print(f"胜率: {results['win_rate']:.2%}")
|
||||
print(f"总盈利: {results['total_profit']:.2f} u")
|
||||
print(f"平均每笔盈利: {results['average_profit']:.2f} u")
|
||||
print("-" * 60)
|
||||
print(f"最大回撤: {results['max_drawdown']:.2f} u")
|
||||
print(f"最大回撤百分比: {results['max_drawdown_percentage']:.2f}%")
|
||||
print(f"最大回撤期间: 第{results['max_drawdown_period'][0] + 1}笔到第{results['max_drawdown_period'][1] + 1}笔交易")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
# 使用示例
|
||||
if __name__ == "__main__":
|
||||
# 你的日志数据
|
||||
log_data = """
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 02:00:00号 做空(跌包涨) 入场=4474.69 出场=4476.69 出场时间=1756664100000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 03:15:00号 做空(跌包涨) 入场=4472.09 出场=4474.09 出场时间=1756668600000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 03:45:00号 做多(涨包跌) 入场=4474.15 出场=4472.15 出场时间=1756670400000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 04:00:00号 做空(跌包涨) 入场=4453.24 出场=4455.24 出场时间=1756671300000 差价=-2.00 盈利=-4.49 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 05:45:00号 做多(涨包跌) 入场=4473.88 出场=4471.88 出场时间=1756677600000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 06:45:00号 做空(跌包涨) 入场=4439.92 出场=4441.92 出场时间=1756681200000 差价=-2.00 盈利=-4.50 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 07:15:00号 做空(跌包涨) 入场=4432.11 出场=4402.11 出场时间=1756683000000 差价=30.00 盈利=67.69 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 08:15:00号 做空(跌包涨) 入场=4384.50 出场=4386.50 出场时间=1756686600000 差价=-2.00 盈利=-4.56 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 08:45:00号 做多(涨包跌) 入场=4393.52 出场=4391.52 出场时间=1756688400000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 09:15:00号 做空(跌包涨) 入场=4391.40 出场=4393.40 出场时间=1756690200000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 09:30:00号 做多(涨包跌) 入场=4405.79 出场=4403.79 出场时间=1756692000000 差价=-2.00 盈利=-4.54 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 12:30:00号 做空(跌包涨) 入场=4386.76 出场=4388.76 出场时间=1756702800000 差价=-2.00 盈利=-4.56 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 13:15:00号 做空(跌包涨) 入场=4372.37 出场=4374.37 出场时间=1756704600000 差价=-2.00 盈利=-4.57 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 13:30:00号 做多(涨包跌) 入场=4390.78 出场=4388.78 出场时间=1756706400000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.364 | INFO | __main__:<module>:216 - 2025-09-01 14:30:00号 做多(涨包跌) 入场=4394.00 出场=4392.00 出场时间=1756710000000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-01 15:30:00号 做多(涨包跌) 入场=4419.10 出场=4449.10 出场时间=1756712700000 差价=30.00 盈利=67.89 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-01 18:30:00号 做空(跌包涨) 入场=4429.31 出场=4399.31 出场时间=1756723500000 差价=30.00 盈利=67.73 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-01 19:15:00号 做空(跌包涨) 入场=4399.49 出场=4401.49 出场时间=1756728000000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-01 20:45:00号 做空(跌包涨) 入场=4395.36 出场=4397.36 出场时间=1756731600000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-01 21:45:00号 做空(跌包涨) 入场=4384.90 出场=4386.90 出场时间=1756735200000 差价=-2.00 盈利=-4.56 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-01 22:15:00号 做多(涨包跌) 入场=4396.00 出场=4394.00 出场时间=1756737000000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-01 22:45:00号 做空(跌包涨) 入场=4389.51 出场=4391.51 出场时间=1756738800000 差价=-2.00 盈利=-4.56 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-01 23:15:00号 做空(跌包涨) 入场=4381.89 出场=4351.89 出场时间=1756741500000 差价=30.00 盈利=68.46 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-02 00:30:00号 做空(跌包涨) 入场=4358.69 出场=4360.69 出场时间=1756745100000 差价=-2.00 盈利=-4.59 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-02 01:00:00号 做空(跌包涨) 入场=4353.30 出场=4323.30 出场时间=1756746900000 差价=30.00 盈利=68.91 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-02 02:15:00号 做多(涨包跌) 入场=4337.86 出场=4335.86 出场时间=1756751400000 差价=-2.00 盈利=-4.61 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-02 03:00:00号 做多(涨包跌) 入场=4345.08 出场=4343.08 出场时间=1756757700000 差价=-2.00 盈利=-4.60 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-02 03:45:00号 做多(涨包跌) 入场=4363.92 出场=4361.92 出场时间=1756756800000 差价=-2.00 盈利=-4.58 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-02 04:15:00号 做空(跌包涨) 入场=4358.28 出场=4328.28 出场时间=1756758600000 差价=30.00 盈利=68.83 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-02 07:00:00号 做多(涨包跌) 入场=4282.53 出场=4312.53 出场时间=1756770300000 差价=30.00 盈利=70.05 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-02 08:45:00号 做空(跌包涨) 入场=4291.71 出场=4293.71 出场时间=1756774800000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-02 09:00:00号 做多(涨包跌) 入场=4302.49 出场=4300.49 出场时间=1756775700000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-02 09:30:00号 做多(涨包跌) 入场=4308.80 出场=4338.80 出场时间=1756777500000 差价=30.00 盈利=69.62 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.365 | INFO | __main__:<module>:216 - 2025-09-02 11:00:00号 做多(涨包跌) 入场=4359.11 出场=4389.11 出场时间=1756782900000 差价=30.00 盈利=68.82 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 12:00:00号 做空(跌包涨) 入场=4365.21 出场=4367.21 出场时间=1756786500000 差价=-2.00 盈利=-4.58 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 12:15:00号 做多(涨包跌) 入场=4374.91 出场=4372.91 出场时间=1756789200000 差价=-2.00 盈利=-4.57 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 13:15:00号 做空(跌包涨) 入场=4377.72 出场=4379.72 出场时间=1756791000000 差价=-2.00 盈利=-4.57 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 14:45:00号 做多(涨包跌) 入场=4406.35 出场=4404.35 出场时间=1756796400000 差价=-2.00 盈利=-4.54 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 15:15:00号 做空(跌包涨) 入场=4391.65 出场=4393.65 出场时间=1756798200000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 15:45:00号 做空(跌包涨) 入场=4384.00 出场=4386.00 出场时间=1756800900000 差价=-2.00 盈利=-4.56 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 16:30:00号 做多(涨包跌) 入场=4394.20 出场=4392.20 出场时间=1756802700000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 17:00:00号 做空(跌包涨) 入场=4389.98 出场=4391.98 出场时间=1756804500000 差价=-2.00 盈利=-4.56 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 18:15:00号 做空(跌包涨) 入场=4394.30 出场=4364.30 出场时间=1756812600000 差价=30.00 盈利=68.27 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 19:00:00号 做多(涨包跌) 入场=4389.96 出场=4387.96 出场时间=1756811700000 差价=-2.00 盈利=-4.56 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 19:15:00号 做空(跌包涨) 入场=4375.68 出场=4345.68 出场时间=1756813500000 差价=30.00 盈利=68.56 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 20:00:00号 做空(跌包涨) 入场=4346.32 出场=4316.32 出场时间=1756816200000 差价=30.00 盈利=69.02 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-02 20:45:00号 做空(跌包涨) 入场=4300.58 出场=4302.58 出场时间=1756818900000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.366 | INFO | __main__:<module>:216 - 2025-09-03 00:30:00号 做空(跌包涨) 入场=4280.64 出场=4282.64 出场时间=1756831500000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.367 | INFO | __main__:<module>:216 - 2025-09-03 01:15:00号 做空(跌包涨) 入场=4289.63 出场=4291.63 出场时间=1756834200000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 01:30:00号 做多(涨包跌) 入场=4307.71 出场=4305.71 出场时间=1756835100000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 02:00:00号 做多(涨包跌) 入场=4317.18 出场=4315.18 出场时间=1756836900000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 02:15:00号 做空(跌包涨) 入场=4293.70 出场=4295.70 出场时间=1756837800000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 03:00:00号 做多(涨包跌) 入场=4294.57 出场=4292.57 出场时间=1756840500000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 03:15:00号 做空(跌包涨) 入场=4271.11 出场=4273.11 出场时间=1756841400000 差价=-2.00 盈利=-4.68 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 03:45:00号 做多(涨包跌) 入场=4275.15 出场=4273.15 出场时间=1756843200000 差价=-2.00 盈利=-4.68 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 04:15:00号 做多(涨包跌) 入场=4294.16 出场=4324.16 出场时间=1756845000000 差价=30.00 盈利=69.86 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 05:15:00号 做多(涨包跌) 入场=4321.16 出场=4319.16 出场时间=1756848600000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 05:30:00号 做空(跌包涨) 入场=4308.71 出场=4310.71 出场时间=1756849500000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 05:45:00号 做多(涨包跌) 入场=4326.63 出场=4324.63 出场时间=1756850400000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 06:15:00号 做空(跌包涨) 入场=4323.50 出场=4325.50 出场时间=1756852200000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 06:45:00号 做多(涨包跌) 入场=4325.63 出场=4323.63 出场时间=1756854000000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 07:30:00号 做空(跌包涨) 入场=4318.13 出场=4320.13 出场时间=1756856700000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 08:00:00号 做多(涨包跌) 入场=4324.58 出场=4322.58 出场时间=1756858500000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 08:30:00号 做多(涨包跌) 入场=4325.28 出场=4323.28 出场时间=1756860300000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 08:45:00号 做空(跌包涨) 入场=4315.84 出场=4285.84 出场时间=1756862100000 差价=30.00 盈利=69.51 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 10:30:00号 做多(涨包跌) 入场=4351.50 出场=4349.50 出场时间=1756867500000 差价=-2.00 盈利=-4.60 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 12:00:00号 做多(涨包跌) 入场=4332.54 出场=4330.54 出场时间=1756872900000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 12:15:00号 做空(跌包涨) 入场=4318.84 出场=4320.84 出场时间=1756873800000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 13:15:00号 做空(跌包涨) 入场=4332.97 出场=4302.97 出场时间=1756879200000 差价=30.00 盈利=69.24 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 14:30:00号 做多(涨包跌) 入场=4314.99 出场=4312.99 出场时间=1756881900000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 15:45:00号 做多(涨包跌) 入场=4309.16 出场=4307.16 出场时间=1756886400000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 16:15:00号 做多(涨包跌) 入场=4321.12 出场=4319.12 出场时间=1756888200000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 16:45:00号 做多(涨包跌) 入场=4323.65 出场=4321.65 出场时间=1756890000000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 17:00:00号 做空(跌包涨) 入场=4316.15 出场=4318.15 出场时间=1756890900000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 17:30:00号 做多(涨包跌) 入场=4318.40 出场=4348.40 出场时间=1756892700000 差价=30.00 盈利=69.47 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 19:00:00号 做多(涨包跌) 入场=4369.61 出场=4367.61 出场时间=1756898100000 差价=-2.00 盈利=-4.58 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 19:45:00号 做多(涨包跌) 入场=4380.09 出场=4378.09 出场时间=1756900800000 差价=-2.00 盈利=-4.57 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 20:00:00号 做空(跌包涨) 入场=4369.58 出场=4371.58 出场时间=1756902600000 差价=-2.00 盈利=-4.58 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 20:45:00号 做空(跌包涨) 入场=4349.99 出场=4351.99 出场时间=1756904400000 差价=-2.00 盈利=-4.60 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 21:45:00号 做多(涨包跌) 入场=4417.77 出场=4447.77 出场时间=1756908000000 差价=30.00 盈利=67.91 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-03 23:45:00号 做多(涨包跌) 入场=4469.03 出场=4467.03 出场时间=1756915200000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-04 01:00:00号 做多(涨包跌) 入场=4464.29 出场=4462.29 出场时间=1756923300000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-04 02:30:00号 做空(跌包涨) 入场=4467.09 出场=4469.09 出场时间=1756928700000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-04 03:45:00号 做多(涨包跌) 入场=4463.71 出场=4461.71 出场时间=1756931400000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-04 05:15:00号 做多(涨包跌) 入场=4465.31 出场=4463.31 出场时间=1756935000000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-04 05:45:00号 做空(跌包涨) 入场=4454.44 出场=4456.44 出场时间=1756936800000 差价=-2.00 盈利=-4.49 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.368 | INFO | __main__:<module>:216 - 2025-09-04 07:15:00号 做空(跌包涨) 入场=4458.63 出场=4460.63 出场时间=1756942200000 差价=-2.00 盈利=-4.49 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 07:45:00号 做空(跌包涨) 入场=4446.35 出场=4448.35 出场时间=1756944000000 差价=-2.00 盈利=-4.50 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 09:15:00号 做空(跌包涨) 入场=4453.05 出场=4455.05 出场时间=1756949400000 差价=-2.00 盈利=-4.49 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 10:15:00号 做空(跌包涨) 入场=4458.70 出场=4428.70 出场时间=1756953000000 差价=30.00 盈利=67.28 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 11:45:00号 做空(跌包涨) 入场=4396.51 出场=4398.51 出场时间=1756958400000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 12:15:00号 做多(涨包跌) 入场=4401.01 出场=4399.01 出场时间=1756960200000 差价=-2.00 盈利=-4.54 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 12:45:00号 做多(涨包跌) 入场=4403.00 出场=4401.00 出场时间=1756962000000 差价=-2.00 盈利=-4.54 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 13:00:00号 做空(跌包涨) 入场=4389.87 出场=4359.87 出场时间=1756966500000 差价=30.00 盈利=68.34 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 14:00:00号 做多(涨包跌) 入场=4372.82 出场=4370.82 出场时间=1756966500000 差价=-2.00 盈利=-4.57 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 14:30:00号 做空(跌包涨) 入场=4366.72 出场=4368.72 出场时间=1756969200000 差价=-2.00 盈利=-4.58 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 16:00:00号 做多(涨包跌) 入场=4368.80 出场=4366.80 出场时间=1756975500000 差价=-2.00 盈利=-4.58 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 16:45:00号 做空(跌包涨) 入场=4373.25 出场=4375.25 出场时间=1756976400000 差价=-2.00 盈利=-4.57 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 17:15:00号 做多(涨包跌) 入场=4380.98 出场=4378.98 出场时间=1756978200000 差价=-2.00 盈利=-4.57 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 17:45:00号 做多(涨包跌) 入场=4384.86 出场=4414.86 出场时间=1756980900000 差价=30.00 盈利=68.42 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 18:45:00号 做多(涨包跌) 入场=4419.81 出场=4417.81 出场时间=1756983600000 差价=-2.00 盈利=-4.53 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 19:00:00号 做空(跌包涨) 入场=4405.14 出场=4407.14 出场时间=1756984500000 差价=-2.00 盈利=-4.54 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.370 | INFO | __main__:<module>:216 - 2025-09-04 19:15:00号 做多(涨包跌) 入场=4423.54 出场=4421.54 出场时间=1756985400000 差价=-2.00 盈利=-4.52 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-04 20:00:00号 做空(跌包涨) 入场=4404.40 出场=4406.40 出场时间=1756988100000 差价=-2.00 盈利=-4.54 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-04 21:00:00号 做多(涨包跌) 入场=4414.04 出场=4412.04 出场时间=1756991700000 差价=-2.00 盈利=-4.53 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-04 21:45:00号 做空(跌包涨) 入场=4381.48 出场=4351.48 出场时间=1756994400000 差价=30.00 盈利=68.47 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-04 23:00:00号 做空(跌包涨) 入场=4339.69 出场=4309.69 出场时间=1756998900000 差价=30.00 盈利=69.13 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 00:15:00号 做多(涨包跌) 入场=4306.10 出场=4304.10 出场时间=1757003400000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 00:30:00号 做空(跌包涨) 入场=4287.51 出场=4289.51 出场时间=1757004300000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 01:15:00号 做多(涨包跌) 入场=4305.80 出场=4303.80 出场时间=1757007000000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 01:45:00号 做空(跌包涨) 入场=4301.01 出场=4303.01 出场时间=1757008800000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 02:30:00号 做空(跌包涨) 入场=4278.68 出场=4280.68 出场时间=1757011500000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 03:00:00号 做空(跌包涨) 入场=4271.51 出场=4273.51 出场时间=1757013300000 差价=-2.00 盈利=-4.68 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 04:15:00号 做多(涨包跌) 入场=4294.31 出场=4324.31 出场时间=1757024100000 差价=30.00 盈利=69.86 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 05:30:00号 做多(涨包跌) 入场=4309.65 出场=4339.65 出场时间=1757025900000 差价=30.00 盈利=69.61 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 06:45:00号 做空(跌包涨) 入场=4315.58 出场=4317.58 出场时间=1757026800000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 07:00:00号 做多(涨包跌) 入场=4339.52 出场=4337.52 出场时间=1757027700000 差价=-2.00 盈利=-4.61 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 09:00:00号 做空(跌包涨) 入场=4298.93 出场=4300.93 出场时间=1757034900000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 11:00:00号 做多(涨包跌) 入场=4321.58 出场=4319.58 出场时间=1757042100000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 11:15:00号 做空(跌包涨) 入场=4303.41 出场=4305.41 出场时间=1757043000000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 11:45:00号 做多(涨包跌) 入场=4312.51 出场=4342.51 出场时间=1757055600000 差价=30.00 盈利=69.57 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.371 | INFO | __main__:<module>:216 - 2025-09-05 13:45:00号 做多(涨包跌) 入场=4336.36 出场=4334.36 出场时间=1757052000000 差价=-2.00 盈利=-4.61 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-05 15:15:00号 做多(涨包跌) 入场=4354.09 出场=4384.09 出场时间=1757057400000 差价=30.00 盈利=68.90 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-05 17:15:00号 做空(跌包涨) 入场=4394.99 出场=4396.99 出场时间=1757064600000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-05 20:00:00号 做空(跌包涨) 入场=4415.56 出场=4417.56 出场时间=1757074500000 差价=-2.00 盈利=-4.53 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-05 21:00:00号 做多(涨包跌) 入场=4469.26 出场=4467.26 出场时间=1757078100000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 00:00:00号 做空(跌包涨) 入场=4280.44 出场=4282.44 出场时间=1757088900000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 00:45:00号 做多(涨包跌) 入场=4297.13 出场=4295.13 出场时间=1757091600000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 02:00:00号 做多(涨包跌) 入场=4287.32 出场=4285.32 出场时间=1757096100000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 02:30:00号 做多(涨包跌) 入场=4295.81 出场=4325.81 出场时间=1757099700000 差价=30.00 盈利=69.84 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 04:00:00号 做多(涨包跌) 入场=4328.97 出场=4326.97 出场时间=1757103300000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 04:45:00号 做多(涨包跌) 入场=4337.01 出场=4335.01 出场时间=1757106000000 差价=-2.00 盈利=-4.61 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 06:00:00号 做空(跌包涨) 入场=4294.51 出场=4296.51 出场时间=1757110500000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 06:45:00号 做多(涨包跌) 入场=4314.65 出场=4312.65 出场时间=1757113200000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 07:45:00号 做空(跌包涨) 入场=4308.19 出场=4310.19 出场时间=1757117700000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 08:30:00号 做多(涨包跌) 入场=4314.27 出场=4312.27 出场时间=1757122200000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 09:30:00号 做空(跌包涨) 入场=4313.82 出场=4315.82 出场时间=1757123100000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 13:00:00号 做多(涨包跌) 入场=4307.45 出场=4305.45 出场时间=1757135700000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.373 | INFO | __main__:<module>:216 - 2025-09-06 13:30:00号 做空(跌包涨) 入场=4295.99 出场=4297.99 出场时间=1757138400000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 14:15:00号 做多(涨包跌) 入场=4299.28 出场=4297.28 出场时间=1757149200000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 15:15:00号 做多(涨包跌) 入场=4306.50 出场=4304.50 出场时间=1757143800000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 15:45:00号 做多(涨包跌) 入场=4307.61 出场=4305.61 出场时间=1757145600000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 16:15:00号 做空(跌包涨) 入场=4305.07 出场=4307.07 出场时间=1757147400000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 17:45:00号 做多(涨包跌) 入场=4296.51 出场=4294.51 出场时间=1757152800000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 18:00:00号 做空(跌包涨) 入场=4291.09 出场=4293.09 出场时间=1757153700000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 18:45:00号 做多(涨包跌) 入场=4301.16 出场=4299.16 出场时间=1757156400000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 19:45:00号 做空(跌包涨) 入场=4293.71 出场=4295.71 出场时间=1757160000000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 20:00:00号 做多(涨包跌) 入场=4298.08 出场=4296.08 出场时间=1757160900000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 20:30:00号 做空(跌包涨) 入场=4297.58 出场=4299.58 出场时间=1757162700000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 21:45:00号 做空(跌包涨) 入场=4291.70 出场=4293.70 出场时间=1757167200000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-06 23:00:00号 做空(跌包涨) 入场=4294.19 出场=4264.19 出场时间=1757173500000 差价=30.00 盈利=69.86 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-07 00:30:00号 做空(跌包涨) 入场=4257.59 出场=4259.59 出场时间=1757177100000 差价=-2.00 盈利=-4.70 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-07 00:45:00号 做多(涨包跌) 入场=4271.27 出场=4269.27 出场时间=1757179800000 差价=-2.00 盈利=-4.68 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-07 03:00:00号 做多(涨包跌) 入场=4276.85 出场=4274.85 出场时间=1757186100000 差价=-2.00 盈利=-4.68 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-07 04:15:00号 做多(涨包跌) 入场=4260.10 出场=4290.10 出场时间=1757206800000 差价=30.00 盈利=70.42 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-07 05:45:00号 做空(跌包涨) 入场=4271.09 出场=4273.09 出场时间=1757196000000 差价=-2.00 盈利=-4.68 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-07 06:00:00号 做多(涨包跌) 入场=4281.89 出场=4279.89 出场时间=1757196900000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-07 07:00:00号 做多(涨包跌) 入场=4275.91 出场=4273.91 出场时间=1757200500000 差价=-2.00 盈利=-4.68 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-07 07:45:00号 做空(跌包涨) 入场=4273.27 出场=4275.27 出场时间=1757203200000 差价=-2.00 盈利=-4.68 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.374 | INFO | __main__:<module>:216 - 2025-09-07 08:15:00号 做多(涨包跌) 入场=4277.06 出场=4275.06 出场时间=1757205000000 差价=-2.00 盈利=-4.68 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.375 | INFO | __main__:<module>:216 - 2025-09-07 08:45:00号 做多(涨包跌) 入场=4283.66 出场=4281.66 出场时间=1757206800000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 09:15:00号 做多(涨包跌) 入场=4289.62 出场=4287.62 出场时间=1757210400000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 10:30:00号 做多(涨包跌) 入场=4293.75 出场=4291.75 出场时间=1757216700000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 11:00:00号 做多(涨包跌) 入场=4297.65 出场=4295.65 出场时间=1757214900000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 11:30:00号 做多(涨包跌) 入场=4300.79 出场=4298.79 出场时间=1757216700000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 13:00:00号 做多(涨包跌) 入场=4299.27 出场=4297.27 出场时间=1757222100000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 13:30:00号 做多(涨包跌) 入场=4300.38 出场=4298.38 出场时间=1757223900000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 13:45:00号 做空(跌包涨) 入场=4292.60 出场=4294.60 出场时间=1757230200000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 14:15:00号 做空(跌包涨) 入场=4288.76 出场=4290.76 出场时间=1757226600000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 14:45:00号 做空(跌包涨) 入场=4284.83 出场=4286.83 出场时间=1757229300000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 16:15:00号 做多(涨包跌) 入场=4299.99 出场=4297.99 出场时间=1757235600000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 17:15:00号 做多(涨包跌) 入场=4304.68 出场=4302.68 出场时间=1757237400000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 17:30:00号 做空(跌包涨) 入场=4299.22 出场=4301.22 出场时间=1757239200000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 18:30:00号 做空(跌包涨) 入场=4297.80 出场=4299.80 出场时间=1757241900000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 19:30:00号 做空(跌包涨) 入场=4301.86 出场=4303.86 出场时间=1757245500000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 20:15:00号 做空(跌包涨) 入场=4298.00 出场=4300.00 出场时间=1757248200000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 21:15:00号 做空(跌包涨) 入场=4294.46 出场=4296.46 出场时间=1757251800000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 22:45:00号 做多(涨包跌) 入场=4309.11 出场=4307.11 出场时间=1757257200000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 23:00:00号 做空(跌包涨) 入场=4298.01 出场=4300.01 出场时间=1757258100000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-07 23:30:00号 做多(涨包跌) 入场=4298.19 出场=4296.19 出场时间=1757259900000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 00:00:00号 做空(跌包涨) 入场=4297.13 出场=4299.13 出场时间=1757283300000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 01:00:00号 做空(跌包涨) 入场=4281.91 出场=4283.91 出场时间=1757271600000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 01:45:00号 做多(涨包跌) 入场=4281.14 出场=4279.14 出场时间=1757268000000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 03:30:00号 做多(涨包跌) 入场=4279.60 出场=4309.60 出场时间=1757286000000 差价=30.00 盈利=70.10 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 06:00:00号 做空(跌包涨) 入场=4286.10 出场=4288.10 出场时间=1757283300000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 06:15:00号 做多(涨包跌) 入场=4294.90 出场=4292.90 出场时间=1757284200000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 07:00:00号 做多(涨包跌) 入场=4308.01 出场=4306.01 出场时间=1757288700000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 08:30:00号 做多(涨包跌) 入场=4314.79 出场=4312.79 出场时间=1757292300000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 10:15:00号 做空(跌包涨) 入场=4289.77 出场=4291.77 出场时间=1757298600000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 10:45:00号 做多(涨包跌) 入场=4301.19 出场=4299.19 出场时间=1757304000000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 11:30:00号 做多(涨包跌) 入场=4307.14 出场=4305.14 出场时间=1757304000000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 12:45:00号 做空(跌包涨) 入场=4301.22 出场=4303.22 出场时间=1757315700000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 13:45:00号 做空(跌包涨) 入场=4287.64 出场=4289.64 出场时间=1757311200000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 14:30:00号 做多(涨包跌) 入场=4299.95 出场=4297.95 出场时间=1757313900000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 15:45:00号 做空(跌包涨) 入场=4293.26 出场=4295.26 出场时间=1757319300000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 17:15:00号 做多(涨包跌) 入场=4315.78 出场=4313.78 出场时间=1757323800000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 17:45:00号 做空(跌包涨) 入场=4310.70 出场=4312.70 出场时间=1757325600000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 19:00:00号 做空(跌包涨) 入场=4327.00 出场=4329.00 出场时间=1757336400000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 20:30:00号 做多(涨包跌) 入场=4313.57 出场=4343.57 出场时间=1757336400000 差价=30.00 盈利=69.55 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-08 21:15:00号 做多(涨包跌) 入场=4357.36 出场=4355.36 出场时间=1757338200000 差价=-2.00 盈利=-4.59 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-09 00:45:00号 做空(跌包涨) 入场=4345.02 出场=4315.02 出场时间=1757353500000 差价=30.00 盈利=69.04 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-09 01:30:00号 做多(涨包跌) 入场=4339.20 出场=4337.20 出场时间=1757353500000 差价=-2.00 盈利=-4.61 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-09 01:45:00号 做空(跌包涨) 入场=4323.36 出场=4325.36 出场时间=1757355300000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-09 03:00:00号 做多(涨包跌) 入场=4327.57 出场=4325.57 出场时间=1757358900000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-09 03:15:00号 做空(跌包涨) 入场=4312.48 出场=4282.48 出场时间=1757359800000 差价=30.00 盈利=69.57 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-09 06:30:00号 做空(跌包涨) 入场=4316.91 出场=4286.91 出场时间=1757377800000 差价=30.00 盈利=69.49 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-09 07:30:00号 做空(跌包涨) 入场=4301.76 出场=4303.76 出场时间=1757375100000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.376 | INFO | __main__:<module>:216 - 2025-09-09 08:00:00号 做多(涨包跌) 入场=4303.99 出场=4301.99 出场时间=1757376900000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 08:30:00号 做空(跌包涨) 入场=4298.46 出场=4300.46 出场时间=1757378700000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 09:00:00号 做多(涨包跌) 入场=4299.61 出场=4297.61 出场时间=1757380500000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 09:15:00号 做空(跌包涨) 入场=4296.12 出场=4298.12 出场时间=1757387700000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 10:00:00号 做空(跌包涨) 入场=4284.98 出场=4286.98 出场时间=1757384100000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 12:00:00号 做多(涨包跌) 入场=4304.98 出场=4302.98 出场时间=1757391300000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 12:30:00号 做多(涨包跌) 入场=4309.18 出场=4307.18 出场时间=1757393100000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 13:00:00号 做多(涨包跌) 入场=4311.76 出场=4309.76 出场时间=1757394900000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 13:15:00号 做空(跌包涨) 入场=4304.94 出场=4306.94 出场时间=1757395800000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 17:30:00号 做多(涨包跌) 入场=4364.44 出场=4362.44 出场时间=1757411100000 差价=-2.00 盈利=-4.58 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 18:30:00号 做空(跌包涨) 入场=4344.37 出场=4346.37 出场时间=1757414700000 差价=-2.00 盈利=-4.60 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 18:45:00号 做多(涨包跌) 入场=4357.24 出场=4355.24 出场时间=1757415600000 差价=-2.00 盈利=-4.59 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 20:15:00号 做多(涨包跌) 入场=4354.99 出场=4352.99 出场时间=1757421000000 差价=-2.00 盈利=-4.59 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 20:45:00号 做空(跌包涨) 入场=4346.93 出场=4348.93 出场时间=1757424600000 差价=-2.00 盈利=-4.60 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.377 | INFO | __main__:<module>:216 - 2025-09-09 23:15:00号 做空(跌包涨) 入场=4289.80 出场=4291.80 出场时间=1757432700000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 01:00:00号 做多(涨包跌) 入场=4286.31 出场=4284.31 出场时间=1757438100000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 01:30:00号 做空(跌包涨) 入场=4282.03 出场=4284.03 出场时间=1757439900000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 02:15:00号 做空(跌包涨) 入场=4283.63 出场=4285.63 出场时间=1757442600000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 02:30:00号 做多(涨包跌) 入场=4288.33 出场=4286.33 出场时间=1757443500000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 02:45:00号 做空(跌包涨) 入场=4280.97 出场=4282.97 出场时间=1757444400000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 04:15:00号 做空(跌包涨) 入场=4285.42 出场=4287.42 出场时间=1757449800000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 05:30:00号 做空(跌包涨) 入场=4297.59 出场=4299.59 出场时间=1757454300000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 06:45:00号 做多(涨包跌) 入场=4313.02 出场=4311.02 出场时间=1757458800000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 07:15:00号 做空(跌包涨) 入场=4310.76 出场=4312.76 出场时间=1757460600000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 07:45:00号 做多(涨包跌) 入场=4313.64 出场=4311.64 出场时间=1757462400000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 08:30:00号 做空(跌包涨) 入场=4296.21 出场=4298.21 出场时间=1757466900000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 09:00:00号 做空(跌包涨) 入场=4284.80 出场=4286.80 出场时间=1757466900000 差价=-2.00 盈利=-4.67 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.378 | INFO | __main__:<module>:216 - 2025-09-10 10:15:00号 做多(涨包跌) 入场=4311.47 出场=4309.47 出场时间=1757473200000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 11:00:00号 做空(跌包涨) 入场=4315.73 出场=4317.73 出场时间=1757474100000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 11:30:00号 做空(跌包涨) 入场=4308.70 出场=4310.70 出场时间=1757475900000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 13:15:00号 做空(跌包涨) 入场=4308.49 出场=4310.49 出场时间=1757482200000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 13:30:00号 做多(涨包跌) 入场=4312.14 出场=4310.14 出场时间=1757483100000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 14:00:00号 做空(跌包涨) 入场=4306.35 出场=4308.35 出场时间=1757484900000 差价=-2.00 盈利=-4.64 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 14:30:00号 做多(涨包跌) 入场=4308.01 出场=4338.01 出场时间=1757506500000 差价=30.00 盈利=69.64 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 15:15:00号 做多(涨包跌) 入场=4317.26 出场=4315.26 出场时间=1757494800000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 16:30:00号 做空(跌包涨) 入场=4327.00 出场=4329.00 出场时间=1757493900000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 17:15:00号 做空(跌包涨) 入场=4316.58 出场=4318.58 出场时间=1757496600000 差价=-2.00 盈利=-4.63 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 18:30:00号 做多(涨包跌) 入场=4327.20 出场=4325.20 出场时间=1757501100000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 19:00:00号 做空(跌包涨) 入场=4325.23 出场=4327.23 出场时间=1757502900000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 19:15:00号 做多(涨包跌) 入场=4330.73 出场=4328.73 出场时间=1757503800000 差价=-2.00 盈利=-4.62 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 20:00:00号 做多(涨包跌) 入场=4329.56 出场=4359.56 出场时间=1757507400000 差价=30.00 盈利=69.29 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 21:15:00号 做多(涨包跌) 入场=4382.99 出场=4380.99 出场时间=1757511000000 差价=-2.00 盈利=-4.56 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.380 | INFO | __main__:<module>:216 - 2025-09-10 21:45:00号 做多(涨包跌) 入场=4386.20 出场=4416.20 出场时间=1757512800000 差价=30.00 盈利=68.40 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.381 | INFO | __main__:<module>:216 - 2025-09-10 23:15:00号 做多(涨包跌) 入场=4421.51 出场=4419.51 出场时间=1757518200000 差价=-2.00 盈利=-4.52 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 00:00:00号 做空(跌包涨) 入场=4403.47 出场=4373.47 出场时间=1757520900000 差价=30.00 盈利=68.13 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 01:30:00号 做空(跌包涨) 入场=4346.68 出场=4348.68 出场时间=1757526300000 差价=-2.00 盈利=-4.60 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 05:15:00号 做多(涨包跌) 入场=4337.51 出场=4335.51 出场时间=1757539800000 差价=-2.00 盈利=-4.61 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 05:45:00号 做多(涨包跌) 入场=4346.90 出场=4344.90 出场时间=1757542500000 差价=-2.00 盈利=-4.60 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 06:30:00号 做空(跌包涨) 入场=4344.22 出场=4346.22 出场时间=1757544300000 差价=-2.00 盈利=-4.60 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 07:30:00号 做空(跌包涨) 入场=4342.82 出场=4344.82 出场时间=1757547900000 差价=-2.00 盈利=-4.61 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 08:15:00号 做空(跌包涨) 入场=4336.86 出场=4338.86 出场时间=1757550600000 差价=-2.00 盈利=-4.61 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 09:00:00号 做多(涨包跌) 入场=4357.50 出场=4387.50 出场时间=1757560500000 差价=30.00 盈利=68.85 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 10:15:00号 做多(涨包跌) 入场=4373.00 出场=4371.00 出场时间=1757557800000 差价=-2.00 盈利=-4.57 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 10:45:00号 做多(涨包跌) 入场=4379.99 出场=4377.99 出场时间=1757559600000 差价=-2.00 盈利=-4.57 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 12:00:00号 做多(涨包跌) 入场=4402.00 出场=4400.00 出场时间=1757567700000 差价=-2.00 盈利=-4.54 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 12:30:00号 做多(涨包跌) 入场=4411.97 出场=4409.97 出场时间=1757565900000 差价=-2.00 盈利=-4.53 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 13:00:00号 做空(跌包涨) 入场=4409.99 出场=4411.99 出场时间=1757567700000 差价=-2.00 盈利=-4.54 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 15:30:00号 做空(跌包涨) 入场=4435.47 出场=4437.47 出场时间=1757576700000 差价=-2.00 盈利=-4.51 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 16:45:00号 做多(涨包跌) 入场=4436.04 出场=4434.04 出场时间=1757581200000 差价=-2.00 盈利=-4.51 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.382 | INFO | __main__:<module>:216 - 2025-09-11 18:00:00号 做多(涨包跌) 入场=4415.02 出场=4445.02 出场时间=1757592000000 差价=30.00 盈利=67.95 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-11 19:00:00号 做多(涨包跌) 入场=4426.37 出场=4424.37 出场时间=1757590200000 差价=-2.00 盈利=-4.52 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-11 19:30:00号 做空(跌包涨) 入场=4425.53 出场=4427.53 出场时间=1757591100000 差价=-2.00 盈利=-4.52 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-11 19:45:00号 做多(涨包跌) 入场=4434.28 出场=4432.28 出场时间=1757592000000 差价=-2.00 盈利=-4.51 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-11 20:15:00号 做多(涨包跌) 入场=4452.46 出场=4450.46 出场时间=1757593800000 差价=-2.00 盈利=-4.49 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-11 20:45:00号 做空(跌包涨) 入场=4413.23 出场=4383.23 出场时间=1757595600000 差价=30.00 盈利=67.98 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-11 21:15:00号 做空(跌包涨) 入场=4390.85 出场=4392.85 出场时间=1757597400000 差价=-2.00 盈利=-4.55 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-11 22:00:00号 做多(涨包跌) 入场=4434.65 出场=4432.65 出场时间=1757600100000 差价=-2.00 盈利=-4.51 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-11 22:45:00号 做多(涨包跌) 入场=4428.48 出场=4426.48 出场时间=1757602800000 差价=-2.00 盈利=-4.52 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 00:30:00号 做空(跌包涨) 入场=4409.53 出场=4411.53 出场时间=1757609100000 差价=-2.00 盈利=-4.54 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 01:15:00号 做多(涨包跌) 入场=4428.67 出场=4426.67 出场时间=1757611800000 差价=-2.00 盈利=-4.52 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 01:45:00号 做多(涨包跌) 入场=4434.41 出场=4432.41 出场时间=1757613600000 差价=-2.00 盈利=-4.51 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 03:30:00号 做多(涨包跌) 入场=4421.36 出场=4419.36 出场时间=1757620800000 差价=-2.00 盈利=-4.52 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 05:45:00号 做多(涨包跌) 入场=4421.45 出场=4451.45 出场时间=1757630700000 差价=30.00 盈利=67.85 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 06:30:00号 做多(涨包跌) 入场=4441.44 出场=4471.44 出场时间=1757637000000 差价=30.00 盈利=67.55 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 08:30:00号 做多(涨包跌) 入场=4461.82 出场=4491.82 出场时间=1757637900000 差价=30.00 盈利=67.24 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 12:00:00号 做多(涨包跌) 入场=4503.64 出场=4533.64 出场时间=1757655900000 差价=30.00 盈利=66.61 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 13:15:00号 做空(跌包涨) 入场=4514.86 出场=4516.86 出场时间=1757655000000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 15:15:00号 做多(涨包跌) 入场=4544.41 出场=4542.41 出场时间=1757662200000 差价=-2.00 盈利=-4.40 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 15:30:00号 做空(跌包涨) 入场=4537.01 出场=4507.01 出场时间=1757670300000 差价=30.00 盈利=66.12 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 17:15:00号 做多(涨包跌) 入场=4522.01 出场=4520.01 出场时间=1757669400000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.383 | INFO | __main__:<module>:216 - 2025-09-12 17:30:00号 做空(跌包涨) 入场=4516.28 出场=4518.28 出场时间=1757670300000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-12 18:00:00号 做空(跌包涨) 入场=4513.69 出场=4515.69 出场时间=1757672100000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-12 18:30:00号 做空(跌包涨) 入场=4509.80 出场=4511.80 出场时间=1757673900000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-12 21:00:00号 做多(涨包跌) 入场=4524.96 出场=4522.96 出场时间=1757682900000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-12 22:00:00号 做多(涨包跌) 入场=4537.98 出场=4567.98 出场时间=1757686500000 差价=30.00 盈利=66.11 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-12 22:45:00号 做空(跌包涨) 入场=4523.25 出场=4525.25 出场时间=1757689200000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-12 23:45:00号 做多(涨包跌) 入场=4539.35 出场=4537.35 出场时间=1757692800000 差价=-2.00 盈利=-4.41 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-13 01:00:00号 做多(涨包跌) 入场=4573.66 出场=4571.66 出场时间=1757697300000 差价=-2.00 盈利=-4.37 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-13 02:30:00号 做多(涨包跌) 入场=4612.59 出场=4610.59 出场时间=1757702700000 差价=-2.00 盈利=-4.34 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-13 03:30:00号 做多(涨包跌) 入场=4627.09 出场=4657.09 出场时间=1757706300000 差价=30.00 盈利=64.84 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-13 04:15:00号 做空(跌包涨) 入场=4640.52 出场=4642.52 出场时间=1757709000000 差价=-2.00 盈利=-4.31 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-13 06:15:00号 做多(涨包跌) 入场=4662.69 出场=4692.69 出场时间=1757716200000 差价=30.00 盈利=64.34 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.384 | INFO | __main__:<module>:216 - 2025-09-13 07:30:00号 做多(涨包跌) 入场=4705.12 出场=4703.12 出场时间=1757720700000 差价=-2.00 盈利=-4.25 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.385 | INFO | __main__:<module>:216 - 2025-09-13 08:00:00号 做多(涨包跌) 入场=4710.83 出场=4708.83 出场时间=1757722500000 差价=-2.00 盈利=-4.25 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 08:45:00号 做多(涨包跌) 入场=4699.24 出场=4729.24 出场时间=1757725200000 差价=30.00 盈利=63.84 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 11:15:00号 做多(涨包跌) 入场=4711.38 出场=4741.38 出场时间=1757734200000 差价=30.00 盈利=63.68 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 12:15:00号 做空(跌包涨) 入场=4736.89 出场=4738.89 出场时间=1757737800000 差价=-2.00 盈利=-4.22 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 12:45:00号 做多(涨包跌) 入场=4745.06 出场=4743.06 出场时间=1757739600000 差价=-2.00 盈利=-4.21 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 14:15:00号 做多(涨包跌) 入场=4726.83 出场=4724.83 出场时间=1757745000000 差价=-2.00 盈利=-4.23 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 14:30:00号 做空(跌包涨) 入场=4713.13 出场=4715.13 出场时间=1757747700000 差价=-2.00 盈利=-4.24 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 15:15:00号 做多(涨包跌) 入场=4712.78 出场=4710.78 出场时间=1757749500000 差价=-2.00 盈利=-4.24 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 16:15:00号 做多(涨包跌) 入场=4716.82 出场=4714.82 出场时间=1757759400000 差价=-2.00 盈利=-4.24 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 16:45:00号 做多(涨包跌) 入场=4722.10 出场=4720.10 出场时间=1757754900000 差价=-2.00 盈利=-4.24 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 18:30:00号 做空(跌包涨) 入场=4718.32 出场=4720.32 出场时间=1757763000000 差价=-2.00 盈利=-4.24 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 21:30:00号 做多(涨包跌) 入场=4722.50 出场=4720.50 出场时间=1757771100000 差价=-2.00 盈利=-4.24 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 21:45:00号 做空(跌包涨) 入场=4710.16 出场=4680.16 出场时间=1757772000000 差价=30.00 盈利=63.69 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 22:30:00号 做多(涨包跌) 入场=4699.16 出场=4697.16 出场时间=1757774700000 差价=-2.00 盈利=-4.26 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-13 23:30:00号 做空(跌包涨) 入场=4679.37 出场=4649.37 出场时间=1757778300000 差价=30.00 盈利=64.11 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-14 04:00:00号 做空(跌包涨) 入场=4635.56 出场=4637.56 出场时间=1757794500000 差价=-2.00 盈利=-4.31 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-14 04:15:00号 做多(涨包跌) 入场=4655.26 出场=4653.26 出场时间=1757795400000 差价=-2.00 盈利=-4.30 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-14 05:00:00号 做多(涨包跌) 入场=4655.22 出场=4653.22 出场时间=1757801700000 差价=-2.00 盈利=-4.30 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.386 | INFO | __main__:<module>:216 - 2025-09-14 06:00:00号 做多(涨包跌) 入场=4661.08 出场=4659.08 出场时间=1757801700000 差价=-2.00 盈利=-4.29 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 07:00:00号 做多(涨包跌) 入场=4656.48 出场=4654.48 出场时间=1757806200000 差价=-2.00 盈利=-4.30 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 08:00:00号 做多(涨包跌) 入场=4664.77 出场=4662.77 出场时间=1757810700000 差价=-2.00 盈利=-4.29 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 09:15:00号 做空(跌包涨) 入场=4653.35 出场=4655.35 出场时间=1757813400000 差价=-2.00 盈利=-4.30 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 11:00:00号 做多(涨包跌) 入场=4681.64 出场=4679.64 出场时间=1757819700000 差价=-2.00 盈利=-4.27 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 11:15:00号 做空(跌包涨) 入场=4673.93 出场=4643.93 出场时间=1757823300000 差价=30.00 盈利=64.19 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 13:15:00号 做多(涨包跌) 入场=4666.86 出场=4664.86 出场时间=1757829600000 差价=-2.00 盈利=-4.29 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 14:15:00号 做空(跌包涨) 入场=4668.51 出场=4670.51 出场时间=1757841300000 差价=-2.00 盈利=-4.28 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 14:45:00号 做空(跌包涨) 入场=4660.54 出场=4662.54 出场时间=1757833200000 差价=-2.00 盈利=-4.29 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 15:15:00号 做空(跌包涨) 入场=4659.38 出场=4661.38 出场时间=1757835000000 差价=-2.00 盈利=-4.29 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 15:45:00号 做空(跌包涨) 入场=4658.02 出场=4660.02 出场时间=1757838600000 差价=-2.00 盈利=-4.29 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 17:00:00号 做空(跌包涨) 入场=4654.18 出场=4656.18 出场时间=1757841300000 差价=-2.00 盈利=-4.30 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 18:00:00号 做空(跌包涨) 入场=4663.76 出场=4665.76 出场时间=1757844900000 差价=-2.00 盈利=-4.29 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 19:30:00号 做空(跌包涨) 入场=4638.18 出场=4640.18 出场时间=1757852100000 差价=-2.00 盈利=-4.31 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 20:30:00号 做多(涨包跌) 入场=4640.66 出场=4638.66 出场时间=1757853900000 差价=-2.00 盈利=-4.31 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 20:45:00号 做空(跌包涨) 入场=4625.76 出场=4627.76 出场时间=1757854800000 差价=-2.00 盈利=-4.32 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 21:15:00号 做空(跌包涨) 入场=4621.87 出场=4623.87 出场时间=1757856600000 差价=-2.00 盈利=-4.33 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 22:00:00号 做空(跌包涨) 入场=4610.66 出场=4612.66 出场时间=1757859300000 差价=-2.00 盈利=-4.34 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 22:15:00号 做多(涨包跌) 入场=4630.71 出场=4628.71 出场时间=1757860200000 差价=-2.00 盈利=-4.32 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 23:15:00号 做空(跌包涨) 入场=4586.03 出场=4588.03 出场时间=1757863800000 差价=-2.00 盈利=-4.36 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.387 | INFO | __main__:<module>:216 - 2025-09-14 23:45:00号 做空(跌包涨) 入场=4584.48 出场=4586.48 出场时间=1757865600000 差价=-2.00 盈利=-4.36 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 00:15:00号 做多(涨包跌) 入场=4586.68 出场=4616.68 出场时间=1757870100000 差价=30.00 盈利=65.41 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 01:00:00号 做空(跌包涨) 入场=4595.18 出场=4597.18 出场时间=1757870100000 差价=-2.00 盈利=-4.35 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 03:15:00号 做多(涨包跌) 入场=4609.78 出场=4607.78 出场时间=1757883600000 差价=-2.00 盈利=-4.34 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 04:00:00号 做空(跌包涨) 入场=4613.99 出场=4615.99 出场时间=1757880900000 差价=-2.00 盈利=-4.33 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 04:15:00号 做多(涨包跌) 入场=4624.18 出场=4622.18 出场时间=1757881800000 差价=-2.00 盈利=-4.33 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 05:00:00号 做空(跌包涨) 入场=4614.96 出场=4616.96 出场时间=1757884500000 差价=-2.00 盈利=-4.33 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 05:30:00号 做空(跌包涨) 入场=4613.57 出场=4615.57 出场时间=1757886300000 差价=-2.00 盈利=-4.34 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 05:45:00号 做多(涨包跌) 入场=4633.00 出场=4631.00 出场时间=1757887200000 差价=-2.00 盈利=-4.32 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 08:30:00号 做多(涨包跌) 入场=4613.20 出场=4611.20 出场时间=1757897100000 差价=-2.00 盈利=-4.34 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 09:30:00号 做多(涨包跌) 入场=4601.91 出场=4631.91 出场时间=1757905200000 差价=30.00 盈利=65.19 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 10:30:00号 做空(跌包涨) 入场=4615.91 出场=4617.91 出场时间=1757904300000 差价=-2.00 盈利=-4.33 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 11:15:00号 做多(涨包跌) 入场=4630.48 出场=4628.48 出场时间=1757907000000 差价=-2.00 盈利=-4.32 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 12:00:00号 做多(涨包跌) 入场=4634.46 出场=4664.46 出场时间=1757916000000 差价=30.00 盈利=64.73 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 13:15:00号 做多(涨包跌) 入场=4648.29 出场=4646.29 出场时间=1757917800000 差价=-2.00 盈利=-4.30 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 14:00:00号 做多(涨包跌) 入场=4660.51 出场=4658.51 出场时间=1757916900000 差价=-2.00 盈利=-4.29 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.388 | INFO | __main__:<module>:216 - 2025-09-15 14:30:00号 做空(跌包涨) 入场=4649.31 出场=4619.31 出场时间=1757918700000 差价=30.00 盈利=64.53 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-15 15:30:00号 做空(跌包涨) 入场=4623.51 出场=4593.51 出场时间=1757922300000 差价=30.00 盈利=64.89 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-15 19:30:00号 做空(跌包涨) 入场=4527.63 出场=4529.63 出场时间=1757936700000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-15 20:30:00号 做空(跌包涨) 入场=4512.65 出场=4514.65 出场时间=1757940300000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-15 21:45:00号 做空(跌包涨) 入场=4495.32 出场=4497.32 出场时间=1757944800000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-15 22:30:00号 做多(涨包跌) 入场=4525.84 出场=4523.84 出场时间=1757947500000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-16 00:00:00号 做多(涨包跌) 入场=4497.47 出场=4495.47 出场时间=1757952900000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-16 00:30:00号 做空(跌包涨) 入场=4490.54 出场=4492.54 出场时间=1757954700000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-16 00:45:00号 做多(涨包跌) 入场=4508.90 出场=4506.90 出场时间=1757955600000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-16 01:30:00号 做空(跌包涨) 入场=4484.02 出场=4486.02 出场时间=1757958300000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-16 02:15:00号 做空(跌包涨) 入场=4492.05 出场=4494.05 出场时间=1757961000000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-16 03:00:00号 做多(涨包跌) 入场=4499.06 出场=4497.06 出场时间=1757963700000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-16 04:15:00号 做多(涨包跌) 入场=4499.05 出场=4529.05 出场时间=1757985300000 差价=30.00 盈利=66.68 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-16 05:15:00号 做多(涨包跌) 入场=4508.91 出场=4506.91 出场时间=1757977200000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-16 06:15:00号 做空(跌包涨) 入场=4519.44 出场=4521.44 出场时间=1757978100000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.389 | INFO | __main__:<module>:216 - 2025-09-16 07:15:00号 做多(涨包跌) 入场=4517.10 出场=4515.10 出场时间=1757980800000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 08:15:00号 做空(跌包涨) 入场=4514.54 出场=4516.54 出场时间=1757984400000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 10:15:00号 做多(涨包跌) 入场=4525.99 出场=4523.99 出场时间=1757989800000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 14:30:00号 做空(跌包涨) 入场=4514.40 出场=4516.40 出场时间=1758009600000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 15:15:00号 做空(跌包涨) 入场=4503.94 出场=4505.94 出场时间=1758007800000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 15:30:00号 做多(涨包跌) 入场=4508.98 出场=4506.98 出场时间=1758011400000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 16:00:00号 做多(涨包跌) 入场=4510.63 出场=4508.63 出场时间=1758011400000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 18:00:00号 做多(涨包跌) 入场=4509.35 出场=4507.35 出场时间=1758017700000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 19:30:00号 做多(涨包跌) 入场=4498.85 出场=4496.85 出场时间=1758023100000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 20:00:00号 做多(涨包跌) 入场=4499.40 出场=4497.40 出场时间=1758024900000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 20:45:00号 做多(涨包跌) 入场=4499.52 出场=4497.52 出场时间=1758027600000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 21:30:00号 做多(涨包跌) 入场=4506.71 出场=4504.71 出场时间=1758030300000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 21:45:00号 做空(跌包涨) 入场=4452.91 出场=4422.91 出场时间=1758031200000 差价=30.00 盈利=67.37 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 22:15:00号 做空(跌包涨) 入场=4432.50 出场=4434.50 出场时间=1758033000000 差价=-2.00 盈利=-4.51 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-16 23:15:00号 做空(跌包涨) 入场=4431.53 出场=4433.53 出场时间=1758036600000 差价=-2.00 盈利=-4.51 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-17 00:45:00号 做空(跌包涨) 入场=4456.88 出场=4458.88 出场时间=1758042000000 差价=-2.00 盈利=-4.49 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.390 | INFO | __main__:<module>:216 - 2025-09-17 01:00:00号 做多(涨包跌) 入场=4461.10 出场=4491.10 出场时间=1758042900000 差价=30.00 盈利=67.25 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 02:30:00号 做空(跌包涨) 入场=4481.50 出场=4483.50 出场时间=1758050100000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 05:15:00号 做空(跌包涨) 入场=4490.49 出场=4492.49 出场时间=1758058200000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 06:30:00号 做空(跌包涨) 入场=4505.59 出场=4507.59 出场时间=1758062700000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 06:45:00号 做多(涨包跌) 入场=4517.88 出场=4515.88 出场时间=1758063600000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 08:15:00号 做空(跌包涨) 入场=4495.76 出场=4497.76 出场时间=1758069000000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 08:30:00号 做多(涨包跌) 入场=4509.69 出场=4507.69 出场时间=1758069900000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 09:30:00号 做多(涨包跌) 入场=4514.84 出场=4512.84 出场时间=1758073500000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 09:45:00号 做空(跌包涨) 入场=4504.59 出场=4506.59 出场时间=1758074400000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 10:30:00号 做多(涨包跌) 入场=4514.12 出场=4544.12 出场时间=1758077100000 差价=30.00 盈利=66.46 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 12:30:00号 做空(跌包涨) 入场=4485.88 出场=4487.88 出场时间=1758085200000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 13:15:00号 做多(涨包跌) 入场=4484.36 出场=4482.36 出场时间=1758087000000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 13:45:00号 做多(涨包跌) 入场=4488.67 出场=4518.67 出场时间=1758089700000 差价=30.00 盈利=66.83 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 15:30:00号 做空(跌包涨) 入场=4539.99 出场=4541.99 出场时间=1758095100000 差价=-2.00 盈利=-4.41 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 16:00:00号 做多(涨包跌) 入场=4542.30 出场=4540.30 出场时间=1758096900000 差价=-2.00 盈利=-4.40 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 16:15:00号 做空(跌包涨) 入场=4515.06 出场=4485.06 出场时间=1758104100000 差价=30.00 盈利=66.44 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 17:00:00号 做多(涨包跌) 入场=4509.28 出场=4507.28 出场时间=1758100500000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.391 | INFO | __main__:<module>:216 - 2025-09-17 17:45:00号 做空(跌包涨) 入场=4501.69 出场=4503.69 出场时间=1758111300000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-17 19:00:00号 做空(跌包涨) 入场=4482.16 出场=4484.16 出场时间=1758107700000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-17 19:15:00号 做多(涨包跌) 入场=4487.54 出场=4485.54 出场时间=1758110400000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-17 20:15:00号 做空(跌包涨) 入场=4488.35 出场=4490.35 出场时间=1758112200000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-17 20:30:00号 做多(涨包跌) 入场=4504.63 出场=4502.63 出场时间=1758113100000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-17 22:30:00号 做空(跌包涨) 入场=4472.54 出场=4474.54 出场时间=1758120300000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-17 23:30:00号 做空(跌包涨) 入场=4480.00 出场=4482.00 出场时间=1758123900000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-18 01:15:00号 做空(跌包涨) 入场=4479.93 出场=4481.93 出场时间=1758130200000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-18 02:00:00号 做空(跌包涨) 入场=4436.71 出场=4438.71 出场时间=1758132900000 差价=-2.00 盈利=-4.51 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-18 02:45:00号 做空(跌包涨) 入场=4440.70 出场=4442.70 出场时间=1758135600000 差价=-2.00 盈利=-4.50 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-18 04:30:00号 做空(跌包涨) 入场=4512.43 出场=4514.43 出场时间=1758141900000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-18 05:15:00号 做多(涨包跌) 入场=4515.31 出场=4545.31 出场时间=1758146400000 差价=30.00 盈利=66.44 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-18 05:45:00号 做多(涨包跌) 入场=4528.32 出场=4526.32 出场时间=1758146400000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-18 06:15:00号 做多(涨包跌) 入场=4546.07 出场=4576.07 出场时间=1758149100000 差价=30.00 盈利=65.99 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-18 08:00:00号 做多(涨包跌) 入场=4588.29 出场=4586.29 出场时间=1758156300000 差价=-2.00 盈利=-4.36 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-18 09:00:00号 做空(跌包涨) 入场=4582.62 出场=4584.62 出场时间=1758158100000 差价=-2.00 盈利=-4.36 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-18 09:15:00号 做多(涨包跌) 入场=4602.25 出场=4632.25 出场时间=1758164400000 差价=30.00 盈利=65.19 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.392 | INFO | __main__:<module>:216 - 2025-09-18 12:30:00号 做空(跌包涨) 入场=4605.82 出场=4607.82 出场时间=1758170700000 差价=-2.00 盈利=-4.34 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.393 | INFO | __main__:<module>:216 - 2025-09-18 13:00:00号 做多(涨包跌) 入场=4609.48 出场=4607.48 出场时间=1758172500000 差价=-2.00 盈利=-4.34 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.393 | INFO | __main__:<module>:216 - 2025-09-18 13:15:00号 做空(跌包涨) 入场=4600.42 出场=4570.42 出场时间=1758175200000 差价=30.00 盈利=65.21 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.393 | INFO | __main__:<module>:216 - 2025-09-18 14:15:00号 做空(跌包涨) 入场=4561.37 出场=4563.37 出场时间=1758177000000 差价=-2.00 盈利=-4.38 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 14:45:00号 做多(涨包跌) 入场=4566.29 出场=4596.29 出场时间=1758184200000 差价=30.00 盈利=65.70 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 16:00:00号 做空(跌包涨) 入场=4573.09 出场=4575.09 出场时间=1758183300000 差价=-2.00 盈利=-4.37 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 16:15:00号 做多(涨包跌) 入场=4581.08 出场=4579.08 出场时间=1758190500000 差价=-2.00 盈利=-4.37 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 17:45:00号 做空(跌包涨) 入场=4590.39 出场=4592.39 出场时间=1758189600000 差价=-2.00 盈利=-4.36 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 18:15:00号 做空(跌包涨) 入场=4585.79 出场=4587.79 出场时间=1758202200000 差价=-2.00 盈利=-4.36 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 19:15:00号 做多(涨包跌) 入场=4579.55 出场=4577.55 出场时间=1758195000000 差价=-2.00 盈利=-4.37 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 19:45:00号 做空(跌包涨) 入场=4573.40 出场=4575.40 出场时间=1758196800000 差价=-2.00 盈利=-4.37 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 20:45:00号 做空(跌包涨) 入场=4576.54 出场=4578.54 出场时间=1758200400000 差价=-2.00 盈利=-4.37 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 21:15:00号 做多(涨包跌) 入场=4579.42 出场=4577.42 出场时间=1758202200000 差价=-2.00 盈利=-4.37 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 21:45:00号 做空(跌包涨) 入场=4571.96 出场=4573.96 出场时间=1758204000000 差价=-2.00 盈利=-4.37 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 22:00:00号 做多(涨包跌) 入场=4586.05 出场=4616.05 出场时间=1758204900000 差价=30.00 盈利=65.42 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-18 22:45:00号 做空(跌包涨) 入场=4598.00 出场=4600.00 出场时间=1758207600000 差价=-2.00 盈利=-4.35 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-19 00:30:00号 做多(涨包跌) 入场=4613.82 出场=4611.82 出场时间=1758214800000 差价=-2.00 盈利=-4.33 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-19 02:00:00号 做多(涨包跌) 入场=4607.20 出场=4605.20 出场时间=1758219300000 差价=-2.00 盈利=-4.34 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-19 03:00:00号 做多(涨包跌) 入场=4616.17 出场=4614.17 出场时间=1758222900000 差价=-2.00 盈利=-4.33 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-19 03:15:00号 做空(跌包涨) 入场=4604.49 出场=4574.49 出场时间=1758236400000 差价=30.00 盈利=65.15 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-19 04:00:00号 做空(跌包涨) 入场=4586.07 出场=4588.07 出场时间=1758226500000 差价=-2.00 盈利=-4.36 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-19 04:15:00号 做多(涨包跌) 入场=4594.26 出场=4592.26 出场时间=1758227400000 差价=-2.00 盈利=-4.35 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.394 | INFO | __main__:<module>:216 - 2025-09-19 04:45:00号 做多(涨包跌) 入场=4600.84 出场=4598.84 出场时间=1758229200000 差价=-2.00 盈利=-4.35 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 05:15:00号 做空(跌包涨) 入场=4592.22 出场=4594.22 出场时间=1758231000000 差价=-2.00 盈利=-4.36 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 06:00:00号 做空(跌包涨) 入场=4592.68 出场=4594.68 出场时间=1758233700000 差价=-2.00 盈利=-4.35 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 06:30:00号 做多(涨包跌) 入场=4593.16 出场=4591.16 出场时间=1758235500000 差价=-2.00 盈利=-4.35 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 07:00:00号 做空(跌包涨) 入场=4589.04 出场=4591.04 出场时间=1758240000000 差价=-2.00 盈利=-4.36 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 07:45:00号 做多(涨包跌) 入场=4583.92 出场=4613.92 出场时间=1758244500000 差价=30.00 盈利=65.45 开仓手续费=5u 平仓手续费=5.03
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 09:30:00号 做多(涨包跌) 入场=4614.98 出场=4612.98 出场时间=1758246300000 差价=-2.00 盈利=-4.33 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 10:15:00号 做空(跌包涨) 入场=4596.00 出场=4598.00 出场时间=1758249000000 差价=-2.00 盈利=-4.35 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 11:00:00号 做空(跌包涨) 入场=4586.87 出场=4588.87 出场时间=1758251700000 差价=-2.00 盈利=-4.36 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 11:15:00号 做多(涨包跌) 入场=4594.12 出场=4592.12 出场时间=1758252600000 差价=-2.00 盈利=-4.35 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 11:30:00号 做空(跌包涨) 入场=4575.51 出场=4545.51 出场时间=1758253500000 差价=30.00 盈利=65.57 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 14:15:00号 做空(跌包涨) 入场=4531.27 出场=4533.27 出场时间=1758263400000 差价=-2.00 盈利=-4.41 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 14:45:00号 做空(跌包涨) 入场=4531.00 出场=4533.00 出场时间=1758266100000 差价=-2.00 盈利=-4.41 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 16:15:00号 做多(涨包跌) 入场=4540.60 出场=4538.60 出场时间=1758270600000 差价=-2.00 盈利=-4.40 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 17:15:00号 做多(涨包跌) 入场=4539.75 出场=4537.75 出场时间=1758274200000 差价=-2.00 盈利=-4.41 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.396 | INFO | __main__:<module>:216 - 2025-09-19 17:30:00号 做空(跌包涨) 入场=4529.53 出场=4531.53 出场时间=1758287700000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-19 18:15:00号 做多(涨包跌) 入场=4526.68 出场=4524.68 出场时间=1758277800000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-19 18:45:00号 做多(涨包跌) 入场=4529.15 出场=4527.15 出场时间=1758279600000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-19 20:30:00号 做空(跌包涨) 入场=4511.06 出场=4513.06 出场时间=1758285900000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-19 20:45:00号 做多(涨包跌) 入场=4516.78 出场=4514.78 出场时间=1758288600000 差价=-2.00 盈利=-4.43 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-19 21:15:00号 做多(涨包跌) 入场=4524.99 出场=4522.99 出场时间=1758288600000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-19 22:00:00号 做多(涨包跌) 入场=4525.81 出场=4523.81 出场时间=1758292200000 差价=-2.00 盈利=-4.42 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 00:15:00号 做多(涨包跌) 入场=4480.10 出场=4478.10 出场时间=1758299400000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 01:00:00号 做空(跌包涨) 入场=4478.90 出场=4448.90 出场时间=1758304800000 差价=30.00 盈利=66.98 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 03:00:00号 做空(跌包涨) 入场=4446.67 出场=4448.67 出场时间=1758309300000 差价=-2.00 盈利=-4.50 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 03:15:00号 做多(涨包跌) 入场=4457.43 出场=4455.43 出场时间=1758310200000 差价=-2.00 盈利=-4.49 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 03:45:00号 做空(跌包涨) 入场=4450.00 出场=4452.00 出场时间=1758312000000 差价=-2.00 盈利=-4.49 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 05:00:00号 做空(跌包涨) 入场=4457.40 出场=4459.40 出场时间=1758316500000 差价=-2.00 盈利=-4.49 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 07:45:00号 做多(涨包跌) 入场=4457.59 出场=4455.59 出场时间=1758331800000 差价=-2.00 盈利=-4.49 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 08:30:00号 做空(跌包涨) 入场=4460.41 出场=4462.41 出场时间=1758329100000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 08:45:00号 做多(涨包跌) 入场=4470.38 出场=4468.38 出场时间=1758330000000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 09:15:00号 做空(跌包涨) 入场=4464.23 出场=4466.23 出场时间=1758331800000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 11:45:00号 做多(涨包跌) 入场=4463.37 出场=4461.37 出场时间=1758340800000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 12:30:00号 做空(跌包涨) 入场=4462.81 出场=4464.81 出场时间=1758343500000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 12:45:00号 做多(涨包跌) 入场=4471.24 出场=4469.24 出场时间=1758344400000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 14:00:00号 做多(涨包跌) 入场=4476.31 出场=4474.31 出场时间=1758348900000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 14:15:00号 做空(跌包涨) 入场=4468.99 出场=4470.99 出场时间=1758349800000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 15:30:00号 做空(跌包涨) 入场=4468.61 出场=4470.61 出场时间=1758354300000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 15:45:00号 做多(涨包跌) 入场=4473.80 出场=4471.80 出场时间=1758357900000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 16:45:00号 做多(涨包跌) 入场=4474.98 出场=4472.98 出场时间=1758358800000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.397 | INFO | __main__:<module>:216 - 2025-09-20 17:00:00号 做空(跌包涨) 入场=4463.04 出场=4465.04 出场时间=1758359700000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-20 17:30:00号 做空(跌包涨) 入场=4463.00 出场=4465.00 出场时间=1758361500000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-20 18:00:00号 做多(涨包跌) 入场=4463.69 出场=4461.69 出场时间=1758365100000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-20 19:15:00号 做多(涨包跌) 入场=4467.67 出场=4465.67 出场时间=1758367800000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-20 21:30:00号 做多(涨包跌) 入场=4467.02 出场=4465.02 出场时间=1758376800000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-20 22:00:00号 做空(跌包涨) 入场=4466.11 出场=4468.11 出场时间=1758378600000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-21 00:15:00号 做多(涨包跌) 入场=4497.37 出场=4495.37 出场时间=1758385800000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-21 00:30:00号 做空(跌包涨) 入场=4492.10 出场=4494.10 出场时间=1758386700000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-21 00:45:00号 做多(涨包跌) 入场=4502.10 出场=4500.10 出场时间=1758387600000 差价=-2.00 盈利=-4.44 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-21 02:15:00号 做空(跌包涨) 入场=4479.14 出场=4481.14 出场时间=1758393000000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-21 05:00:00号 做空(跌包涨) 入场=4481.76 出场=4483.76 出场时间=1758402900000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-21 05:15:00号 做多(涨包跌) 入场=4489.11 出场=4487.11 出场时间=1758404700000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-21 06:45:00号 做空(跌包涨) 入场=4487.79 出场=4489.79 出场时间=1758409200000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-21 07:15:00号 做空(跌包涨) 入场=4486.31 出场=4456.31 出场时间=1758420900000 差价=30.00 盈利=66.87 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-21 08:15:00号 做多(涨包跌) 入场=4480.64 出场=4478.64 出场时间=1758414600000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-21 08:30:00号 做空(跌包涨) 入场=4474.88 出场=4476.88 出场时间=1758415500000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.399 | INFO | __main__:<module>:216 - 2025-09-21 08:45:00号 做多(涨包跌) 入场=4480.98 出场=4478.98 出场时间=1758418200000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 09:45:00号 做空(跌包涨) 入场=4479.33 出场=4481.33 出场时间=1758420000000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 10:15:00号 做多(涨包跌) 入场=4484.00 出场=4482.00 出场时间=1758421800000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 10:30:00号 做空(跌包涨) 入场=4473.56 出场=4475.56 出场时间=1758422700000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 12:00:00号 做多(涨包跌) 入场=4476.07 出场=4474.07 出场时间=1758428100000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 12:15:00号 做空(跌包涨) 入场=4468.37 出场=4470.37 出场时间=1758429000000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 12:45:00号 做多(涨包跌) 入场=4469.16 出场=4467.16 出场时间=1758430800000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 13:15:00号 做空(跌包涨) 入场=4465.52 出场=4467.52 出场时间=1758432600000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 14:45:00号 做多(涨包跌) 入场=4489.61 出场=4487.61 出场时间=1758438000000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 16:00:00号 做空(跌包涨) 入场=4475.86 出场=4445.86 出场时间=1758447000000 差价=30.00 盈利=67.03 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 20:00:00号 做空(跌包涨) 入场=4458.25 出场=4460.25 出场时间=1758456900000 差价=-2.00 盈利=-4.49 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 20:15:00号 做多(涨包跌) 入场=4463.00 出场=4461.00 出场时间=1758458700000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 20:45:00号 做空(跌包涨) 入场=4462.99 出场=4464.99 出场时间=1758459600000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 22:00:00号 做多(涨包跌) 入场=4473.99 出场=4471.99 出场时间=1758465000000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 22:30:00号 做空(跌包涨) 入场=4473.49 出场=4475.49 出场时间=1758465900000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 22:45:00号 做多(涨包跌) 入场=4477.45 出场=4475.45 出场时间=1758466800000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 23:00:00号 做空(跌包涨) 入场=4470.65 出场=4472.65 出场时间=1758467700000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 23:15:00号 做多(涨包跌) 入场=4480.89 出场=4478.89 出场时间=1758468600000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-21 23:45:00号 做多(涨包跌) 入场=4482.23 出场=4480.23 出场时间=1758471300000 差价=-2.00 盈利=-4.46 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.400 | INFO | __main__:<module>:216 - 2025-09-22 00:45:00号 做空(跌包涨) 入场=4468.98 出场=4470.98 出场时间=1758474000000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 02:45:00号 做多(涨包跌) 入场=4490.40 出场=4488.40 出场时间=1758481200000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 03:15:00号 做空(跌包涨) 入场=4478.59 出场=4480.59 出场时间=1758483000000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 04:00:00号 做多(涨包跌) 入场=4492.20 出场=4490.20 出场时间=1758485700000 差价=-2.00 盈利=-4.45 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 04:45:00号 做空(跌包涨) 入场=4477.81 出场=4447.81 出场时间=1758492900000 差价=30.00 盈利=67.00 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 05:30:00号 做空(跌包涨) 入场=4465.73 出场=4467.73 出场时间=1758491100000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 05:45:00号 做多(涨包跌) 入场=4474.17 出场=4472.17 出场时间=1758492000000 差价=-2.00 盈利=-4.47 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 06:45:00号 做多(涨包跌) 入场=4464.29 出场=4462.29 出场时间=1758495600000 差价=-2.00 盈利=-4.48 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 08:00:00号 做空(跌包涨) 入场=4443.01 出场=4445.01 出场时间=1758500100000 差价=-2.00 盈利=-4.50 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 08:30:00号 做空(跌包涨) 入场=4425.29 出场=4395.29 出场时间=1758501900000 差价=30.00 盈利=67.79 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 09:45:00号 做多(涨包跌) 入场=4343.62 出场=4341.62 出场时间=1758506400000 差价=-2.00 盈利=-4.60 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 10:00:00号 做空(跌包涨) 入场=4325.73 出场=4295.73 出场时间=1758507300000 差价=30.00 盈利=69.35 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 10:30:00号 做空(跌包涨) 入场=4307.52 出场=4277.52 出场时间=1758519000000 差价=30.00 盈利=69.65 开仓手续费=5u 平仓手续费=4.97
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 11:15:00号 做多(涨包跌) 入场=4296.63 出场=4294.63 出场时间=1758511800000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 11:45:00号 做空(跌包涨) 入场=4292.42 出场=4294.42 出场时间=1758513600000 差价=-2.00 盈利=-4.66 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.402 | INFO | __main__:<module>:216 - 2025-09-22 12:00:00号 做多(涨包跌) 入场=4303.62 出场=4301.62 出场时间=1758514500000 差价=-2.00 盈利=-4.65 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-22 13:45:00号 做空(跌包涨) 入场=4277.81 出场=4247.81 出场时间=1758520800000 差价=30.00 盈利=70.13 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-22 16:00:00号 做空(跌包涨) 入场=4191.65 出场=4193.65 出场时间=1758528900000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-22 16:45:00号 做多(涨包跌) 入场=4198.38 出场=4196.38 出场时间=1758531600000 差价=-2.00 盈利=-4.76 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-22 18:45:00号 做空(跌包涨) 入场=4156.51 出场=4158.51 出场时间=1758538800000 差价=-2.00 盈利=-4.81 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-22 19:00:00号 做多(涨包跌) 入场=4179.99 出场=4177.99 出场时间=1758539700000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-22 19:45:00号 做多(涨包跌) 入场=4180.81 出场=4178.81 出场时间=1758542400000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-22 20:30:00号 做多(涨包跌) 入场=4181.83 出场=4179.83 出场时间=1758545100000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-22 21:00:00号 做多(涨包跌) 入场=4190.34 出场=4188.34 出场时间=1758547800000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-22 22:00:00号 做多(涨包跌) 入场=4205.46 出场=4203.46 出场时间=1758550500000 差价=-2.00 盈利=-4.76 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-22 22:30:00号 做多(涨包跌) 入场=4214.47 出场=4212.47 出场时间=1758552300000 差价=-2.00 盈利=-4.75 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-22 23:45:00号 做空(跌包涨) 入场=4168.92 出场=4170.92 出场时间=1758556800000 差价=-2.00 盈利=-4.80 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-23 00:15:00号 做多(涨包跌) 入场=4177.10 出场=4175.10 出场时间=1758558600000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-23 01:30:00号 做多(涨包跌) 入场=4165.93 出场=4163.93 出场时间=1758563100000 差价=-2.00 盈利=-4.80 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-23 02:00:00号 做空(跌包涨) 入场=4162.98 出场=4164.98 出场时间=1758564900000 差价=-2.00 盈利=-4.80 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.404 | INFO | __main__:<module>:216 - 2025-09-23 02:15:00号 做多(涨包跌) 入场=4171.60 出场=4169.60 出场时间=1758565800000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 04:15:00号 做多(涨包跌) 入场=4149.68 出场=4179.68 出场时间=1758573900000 差价=30.00 盈利=72.29 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 05:30:00号 做多(涨包跌) 入场=4186.81 出场=4184.81 出场时间=1758578400000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 06:15:00号 做空(跌包涨) 入场=4189.66 出场=4191.66 出场时间=1758580200000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 07:15:00号 做多(涨包跌) 入场=4195.96 出场=4193.96 出场时间=1758584700000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 08:00:00号 做空(跌包涨) 入场=4196.86 出场=4198.86 出场时间=1758586500000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 08:15:00号 做多(涨包跌) 入场=4203.24 出场=4201.24 出场时间=1758587400000 差价=-2.00 盈利=-4.76 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 08:45:00号 做空(跌包涨) 入场=4193.50 出场=4195.50 出场时间=1758590100000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 09:30:00号 做多(涨包跌) 入场=4195.96 出场=4193.96 出场时间=1758591900000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 09:45:00号 做空(跌包涨) 入场=4171.89 出场=4173.89 出场时间=1758592800000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 10:15:00号 做多(涨包跌) 入场=4175.71 出场=4173.71 出场时间=1758594600000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 10:30:00号 做空(跌包涨) 入场=4157.11 出场=4159.11 出场时间=1758595500000 差价=-2.00 盈利=-4.81 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 11:00:00号 做空(跌包涨) 入场=4141.99 出场=4143.99 出场时间=1758597300000 差价=-2.00 盈利=-4.83 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 11:30:00号 做多(涨包跌) 入场=4175.25 出场=4173.25 出场时间=1758600900000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 12:15:00号 做空(跌包涨) 入场=4175.15 出场=4177.15 出场时间=1758602700000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 14:00:00号 做空(跌包涨) 入场=4181.07 出场=4183.07 出场时间=1758608100000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 14:15:00号 做多(涨包跌) 入场=4192.78 出场=4222.78 出场时间=1758618000000 差价=30.00 盈利=71.55 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 15:30:00号 做空(跌包涨) 入场=4199.93 出场=4201.93 出场时间=1758613500000 差价=-2.00 盈利=-4.76 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 15:45:00号 做多(涨包跌) 入场=4205.71 出场=4203.71 出场时间=1758614400000 差价=-2.00 盈利=-4.76 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 17:00:00号 做空(跌包涨) 入场=4206.84 出场=4208.84 出场时间=1758618900000 差价=-2.00 盈利=-4.75 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 17:15:00号 做多(涨包跌) 入场=4219.98 出场=4217.98 出场时间=1758619800000 差价=-2.00 盈利=-4.74 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 17:30:00号 做空(跌包涨) 入场=4202.54 出场=4204.54 出场时间=1758634200000 差价=-2.00 盈利=-4.76 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 18:30:00号 做空(跌包涨) 入场=4188.92 出场=4190.92 出场时间=1758624300000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 19:00:00号 做多(涨包跌) 入场=4191.02 出场=4189.02 出场时间=1758626100000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 19:15:00号 做空(跌包涨) 入场=4182.13 出场=4184.13 出场时间=1758627000000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.405 | INFO | __main__:<module>:216 - 2025-09-23 20:15:00号 做多(涨包跌) 入场=4193.35 出场=4191.35 出场时间=1758630600000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-23 20:30:00号 做空(跌包涨) 入场=4184.09 出场=4186.09 出场时间=1758631500000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-23 20:45:00号 做多(涨包跌) 入场=4196.15 出场=4194.15 出场时间=1758632400000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-23 21:30:00号 做多(涨包跌) 入场=4194.30 出场=4192.30 出场时间=1758635100000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-23 21:45:00号 做空(跌包涨) 入场=4186.77 出场=4188.77 出场时间=1758636000000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-23 22:15:00号 做多(涨包跌) 入场=4196.04 出场=4194.04 出场时间=1758637800000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-23 22:30:00号 做空(跌包涨) 入场=4182.60 出场=4184.60 出场时间=1758638700000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-23 23:00:00号 做空(跌包涨) 入场=4172.55 出场=4174.55 出场时间=1758640500000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-23 23:45:00号 做空(跌包涨) 入场=4178.60 出场=4180.60 出场时间=1758643200000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-24 00:15:00号 做空(跌包涨) 入场=4172.91 出场=4174.91 出场时间=1758645000000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-24 00:30:00号 做多(涨包跌) 入场=4186.91 出场=4184.91 出场时间=1758645900000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-24 00:45:00号 做空(跌包涨) 入场=4170.16 出场=4172.16 出场时间=1758646800000 差价=-2.00 盈利=-4.80 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-24 01:00:00号 做多(涨包跌) 入场=4190.21 出场=4188.21 出场时间=1758647700000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-24 02:45:00号 做空(跌包涨) 入场=4149.15 出场=4151.15 出场时间=1758654000000 差价=-2.00 盈利=-4.82 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-24 03:45:00号 做空(跌包涨) 入场=4155.56 出场=4157.56 出场时间=1758657600000 差价=-2.00 盈利=-4.81 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.406 | INFO | __main__:<module>:216 - 2025-09-24 04:15:00号 做多(涨包跌) 入场=4157.54 出场=4155.54 出场时间=1758659400000 差价=-2.00 盈利=-4.81 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 04:30:00号 做空(跌包涨) 入场=4149.61 出场=4151.61 出场时间=1758660300000 差价=-2.00 盈利=-4.82 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 04:45:00号 做多(涨包跌) 入场=4163.34 出场=4161.34 出场时间=1758669300000 差价=-2.00 盈利=-4.80 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 06:45:00号 做多(涨包跌) 入场=4182.21 出场=4180.21 出场时间=1758668400000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 09:15:00号 做多(涨包跌) 入场=4182.12 出场=4180.12 出场时间=1758679200000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 10:15:00号 做空(跌包涨) 入场=4168.31 出场=4170.31 出场时间=1758681000000 差价=-2.00 盈利=-4.80 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 12:15:00号 做空(跌包涨) 入场=4098.12 出场=4100.12 出场时间=1758688200000 差价=-2.00 盈利=-4.88 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 16:15:00号 做多(涨包跌) 入场=4174.81 出场=4172.81 出场时间=1758702600000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 16:30:00号 做空(跌包涨) 入场=4168.12 出场=4170.12 出场时间=1758703500000 差价=-2.00 盈利=-4.80 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 16:45:00号 做多(涨包跌) 入场=4175.13 出场=4173.13 出场时间=1758704400000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 17:30:00号 做多(涨包跌) 入场=4174.40 出场=4172.40 出场时间=1758708900000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 18:15:00号 做空(跌包涨) 入场=4177.63 出场=4179.63 出场时间=1758711600000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-24 20:45:00号 做多(涨包跌) 入场=4181.50 出场=4179.50 出场时间=1758718800000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-25 00:30:00号 做多(涨包跌) 入场=4167.86 出场=4165.86 出场时间=1758733200000 差价=-2.00 盈利=-4.80 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-25 01:15:00号 做多(涨包跌) 入场=4178.77 出场=4176.77 出场时间=1758735000000 差价=-2.00 盈利=-4.79 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.407 | INFO | __main__:<module>:216 - 2025-09-25 01:45:00号 做多(涨包跌) 入场=4186.01 出场=4184.01 出场时间=1758736800000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 07:00:00号 做空(跌包涨) 入场=4151.98 出场=4153.98 出场时间=1758755700000 差价=-2.00 盈利=-4.82 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 07:15:00号 做多(涨包跌) 入场=4158.00 出场=4156.00 出场时间=1758756600000 差价=-2.00 盈利=-4.81 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 08:30:00号 做空(跌包涨) 入场=4137.01 出场=4139.01 出场时间=1758761100000 差价=-2.00 盈利=-4.83 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 09:00:00号 做空(跌包涨) 入场=4126.96 出场=4096.96 出场时间=1758762900000 差价=30.00 盈利=72.69 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 10:45:00号 做多(涨包跌) 入场=4099.28 出场=4097.28 出场时间=1758769200000 差价=-2.00 盈利=-4.88 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 11:15:00号 做空(跌包涨) 入场=4086.56 出场=4056.56 出场时间=1758771000000 差价=30.00 盈利=73.41 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 12:15:00号 做空(跌包涨) 入场=4045.83 出场=4015.83 出场时间=1758774600000 差价=30.00 盈利=74.15 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 13:00:00号 做空(跌包涨) 入场=4000.17 出场=4002.17 出场时间=1758777300000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 16:00:00号 做空(跌包涨) 入场=4006.93 出场=4008.93 出场时间=1758788100000 差价=-2.00 盈利=-4.99 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 16:15:00号 做多(涨包跌) 入场=4029.59 出场=4027.59 出场时间=1758789000000 差价=-2.00 盈利=-4.96 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 16:45:00号 做空(跌包涨) 入场=4010.29 出场=4012.29 出场时间=1758790800000 差价=-2.00 盈利=-4.99 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 19:45:00号 做空(跌包涨) 入场=3998.02 出场=4000.02 出场时间=1758801600000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 20:30:00号 做空(跌包涨) 入场=3939.54 出场=3941.54 出场时间=1758804300000 差价=-2.00 盈利=-5.08 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 21:30:00号 做多(涨包跌) 入场=4001.35 出场=3999.35 出场时间=1758807900000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-25 21:45:00号 做空(跌包涨) 入场=3957.31 出场=3959.31 出场时间=1758808800000 差价=-2.00 盈利=-5.05 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-26 00:15:00号 做空(跌包涨) 入场=3996.68 出场=3966.68 出场时间=1758817800000 差价=30.00 盈利=75.06 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-26 01:45:00号 做空(跌包涨) 入场=3865.17 出场=3835.17 出场时间=1758823200000 差价=30.00 盈利=77.62 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.408 | INFO | __main__:<module>:216 - 2025-09-26 03:30:00号 做多(涨包跌) 入场=3937.43 出场=3935.43 出场时间=1758829500000 差价=-2.00 盈利=-5.08 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 04:45:00号 做空(跌包涨) 入场=3891.53 出场=3893.53 出场时间=1758834000000 差价=-2.00 盈利=-5.14 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 05:15:00号 做多(涨包跌) 入场=3898.34 出场=3896.34 出场时间=1758835800000 差价=-2.00 盈利=-5.13 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 06:00:00号 做多(涨包跌) 入场=3903.57 出场=3933.57 出场时间=1758838500000 差价=30.00 盈利=76.85 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 08:45:00号 做空(跌包涨) 入场=3872.95 出场=3874.95 出场时间=1758848400000 差价=-2.00 盈利=-5.16 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 09:00:00号 做多(涨包跌) 入场=3930.76 出场=3928.76 出场时间=1758849300000 差价=-2.00 盈利=-5.09 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 09:30:00号 做空(跌包涨) 入场=3927.77 出场=3929.77 出场时间=1758851100000 差价=-2.00 盈利=-5.09 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 09:45:00号 做多(涨包跌) 入场=3941.60 出场=3971.60 出场时间=1758860100000 差价=30.00 盈利=76.11 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 10:45:00号 做多(涨包跌) 入场=3957.81 出场=3955.81 出场时间=1758855600000 差价=-2.00 盈利=-5.05 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 11:15:00号 做多(涨包跌) 入场=3959.57 出场=3957.57 出场时间=1758857400000 差价=-2.00 盈利=-5.05 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 11:30:00号 做空(跌包涨) 入场=3954.21 出场=3956.21 出场时间=1758858300000 差价=-2.00 盈利=-5.06 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 12:30:00号 做空(跌包涨) 入场=3960.89 出场=3930.89 出场时间=1758865500000 差价=30.00 盈利=75.74 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 13:45:00号 做空(跌包涨) 入场=3942.77 出场=3912.77 出场时间=1758866400000 差价=30.00 盈利=76.09 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 15:00:00号 做空(跌包涨) 入场=3919.00 出场=3921.00 出场时间=1758870900000 差价=-2.00 盈利=-5.10 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 16:00:00号 做多(涨包跌) 入场=3936.60 出场=3934.60 出场时间=1758874500000 差价=-2.00 盈利=-5.08 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.409 | INFO | __main__:<module>:216 - 2025-09-26 16:45:00号 做多(涨包跌) 入场=3937.94 出场=3935.94 出场时间=1758877200000 差价=-2.00 盈利=-5.08 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-26 17:00:00号 做空(跌包涨) 入场=3932.27 出场=3934.27 出场时间=1758878100000 差价=-2.00 盈利=-5.09 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-26 17:30:00号 做空(跌包涨) 入场=3925.74 出场=3895.74 出场时间=1758880800000 差价=30.00 盈利=76.42 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-26 18:15:00号 做空(跌包涨) 入场=3885.27 出场=3887.27 出场时间=1758882600000 差价=-2.00 盈利=-5.15 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-26 18:45:00号 做多(涨包跌) 入场=3886.20 出场=3884.20 出场时间=1758884400000 差价=-2.00 盈利=-5.15 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-26 19:15:00号 做空(跌包涨) 入场=3875.50 出场=3877.50 出场时间=1758886200000 差价=-2.00 盈利=-5.16 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-26 19:30:00号 做多(涨包跌) 入场=3888.98 出场=3886.98 出场时间=1758888000000 差价=-2.00 盈利=-5.14 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-26 21:45:00号 做多(涨包跌) 入场=3937.17 出场=3967.17 出场时间=1758895200000 差价=30.00 盈利=76.20 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-26 23:00:00号 做空(跌包涨) 入场=3929.62 出场=3931.62 出场时间=1758899700000 差价=-2.00 盈利=-5.09 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-27 00:15:00号 做多(涨包跌) 入场=3962.86 出场=3992.86 出场时间=1758906000000 差价=30.00 盈利=75.70 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-27 02:45:00号 做空(跌包涨) 入场=4040.09 出场=4042.09 出场时间=1758913200000 差价=-2.00 盈利=-4.95 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-27 03:15:00号 做空(跌包涨) 入场=4036.73 出场=4006.73 出场时间=1758915000000 差价=30.00 盈利=74.32 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-27 04:00:00号 做多(涨包跌) 入场=4021.39 出场=4019.39 出场时间=1758917700000 差价=-2.00 盈利=-4.97 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-27 04:30:00号 做空(跌包涨) 入场=4016.00 出场=4018.00 出场时间=1758922200000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-27 05:15:00号 做多(涨包跌) 入场=4014.71 出场=4012.71 出场时间=1758922200000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.411 | INFO | __main__:<module>:216 - 2025-09-27 05:45:00号 做空(跌包涨) 入场=4010.24 出场=4012.24 出场时间=1758924000000 差价=-2.00 盈利=-4.99 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 06:00:00号 做多(涨包跌) 入场=4021.35 出场=4019.35 出场时间=1758927600000 差价=-2.00 盈利=-4.97 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 07:00:00号 做空(跌包涨) 入场=4024.56 出场=4026.56 出场时间=1758929400000 差价=-2.00 盈利=-4.97 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 08:45:00号 做空(跌包涨) 入场=4020.15 出场=4022.15 出场时间=1758934800000 差价=-2.00 盈利=-4.97 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 10:00:00号 做多(涨包跌) 入场=4016.68 出场=4014.68 出场时间=1758939300000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 10:30:00号 做空(跌包涨) 入场=4011.24 出场=4013.24 出场时间=1758941100000 差价=-2.00 盈利=-4.99 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 11:00:00号 做空(跌包涨) 入场=4010.82 出场=4012.82 出场时间=1758942900000 差价=-2.00 盈利=-4.99 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 11:30:00号 做多(涨包跌) 入场=4021.64 出场=4019.64 出场时间=1758944700000 差价=-2.00 盈利=-4.97 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 12:00:00号 做空(跌包涨) 入场=4017.19 出场=4019.19 出场时间=1758946500000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 12:30:00号 做多(涨包跌) 入场=4024.64 出场=4022.64 出场时间=1758948300000 差价=-2.00 盈利=-4.97 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 13:45:00号 做多(涨包跌) 入场=4017.61 出场=4015.61 出场时间=1758952800000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 14:15:00号 做空(跌包涨) 入场=4013.01 出场=4015.01 出场时间=1758954600000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 14:45:00号 做多(涨包跌) 入场=4014.09 出场=4012.09 出场时间=1758957300000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 15:30:00号 做空(跌包涨) 入场=4011.76 出场=3981.76 出场时间=1758962700000 差价=30.00 盈利=74.78 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 18:45:00号 做多(涨包跌) 入场=4011.07 出场=4009.07 出场时间=1758970800000 差价=-2.00 盈利=-4.99 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.413 | INFO | __main__:<module>:216 - 2025-09-27 19:30:00号 做多(涨包跌) 入场=4011.08 出场=4009.08 出场时间=1758973500000 差价=-2.00 盈利=-4.99 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-27 19:45:00号 做空(跌包涨) 入场=4003.03 出场=4005.03 出场时间=1758976200000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-27 22:00:00号 做空(跌包涨) 入场=4018.39 出场=4020.39 出场时间=1758982500000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-27 22:30:00号 做空(跌包涨) 入场=4017.14 出场=4019.14 出场时间=1758987900000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 00:15:00号 做空(跌包涨) 入场=4010.03 出场=4012.03 出场时间=1758990600000 差价=-2.00 盈利=-4.99 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 01:45:00号 做多(涨包跌) 入场=3994.29 出场=3992.29 出场时间=1758996000000 差价=-2.00 盈利=-5.01 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 03:00:00号 做多(涨包跌) 入场=3994.64 出场=3992.64 出场时间=1759000500000 差价=-2.00 盈利=-5.01 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 03:30:00号 做多(涨包跌) 入场=3997.57 出场=3995.57 出场时间=1759002300000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 04:00:00号 做多(涨包跌) 入场=4000.64 出场=4030.64 出场时间=1759011300000 差价=30.00 盈利=74.99 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 05:45:00号 做多(涨包跌) 入场=4014.84 出场=4012.84 出场时间=1759010400000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 06:15:00号 做多(涨包跌) 入场=4021.22 出场=4019.22 出场时间=1759012200000 差价=-2.00 盈利=-4.97 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 06:45:00号 做空(跌包涨) 入场=4017.93 出场=4019.93 出场时间=1759014000000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 07:30:00号 做空(跌包涨) 入场=4016.91 出场=4018.91 出场时间=1759016700000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 08:00:00号 做多(涨包跌) 入场=4016.99 出场=4014.99 出场时间=1759018500000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 08:15:00号 做空(跌包涨) 入场=4011.14 出场=4013.14 出场时间=1759019400000 差价=-2.00 盈利=-4.99 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.415 | INFO | __main__:<module>:216 - 2025-09-28 08:30:00号 做多(涨包跌) 入场=4027.69 出场=4025.69 出场时间=1759020300000 差价=-2.00 盈利=-4.97 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 09:45:00号 做空(跌包涨) 入场=3995.87 出场=3997.87 出场时间=1759024800000 差价=-2.00 盈利=-5.01 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 10:30:00号 做多(涨包跌) 入场=4002.88 出场=4000.88 出场时间=1759027500000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 11:15:00号 做多(涨包跌) 入场=4003.08 出场=4001.08 出场时间=1759030200000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 12:15:00号 做空(跌包涨) 入场=3996.98 出场=3998.98 出场时间=1759033800000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 12:45:00号 做多(涨包跌) 入场=4002.57 出场=4000.57 出场时间=1759040100000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 13:15:00号 做空(跌包涨) 入场=4001.69 出场=4003.69 出场时间=1759037400000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 13:30:00号 做多(涨包跌) 入场=4009.87 出场=4007.87 出场时间=1759038300000 差价=-2.00 盈利=-4.99 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 14:00:00号 做空(跌包涨) 入场=4008.99 出场=4010.99 出场时间=1759040100000 差价=-2.00 盈利=-4.99 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 14:30:00号 做空(跌包涨) 入场=3998.62 出场=4000.62 出场时间=1759041900000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 16:15:00号 做空(跌包涨) 入场=4010.49 出场=3980.49 出场时间=1759062600000 差价=30.00 盈利=74.80 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 18:00:00号 做空(跌包涨) 入场=3993.77 出场=3995.77 出场时间=1759055400000 差价=-2.00 盈利=-5.01 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 18:45:00号 做多(涨包跌) 入场=3996.14 出场=3994.14 出场时间=1759057200000 差价=-2.00 盈利=-5.00 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 20:45:00号 做空(跌包涨) 入场=3969.72 出场=3971.72 出场时间=1759064400000 差价=-2.00 盈利=-5.04 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 23:00:00号 做多(涨包跌) 入场=4022.99 出场=4020.99 出场时间=1759072500000 差价=-2.00 盈利=-4.97 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-28 23:45:00号 做空(跌包涨) 入场=4019.98 出场=4021.98 出场时间=1759075200000 差价=-2.00 盈利=-4.98 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-29 00:15:00号 做多(涨包跌) 入场=4032.80 出场=4030.80 出场时间=1759077000000 差价=-2.00 盈利=-4.96 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.416 | INFO | __main__:<module>:216 - 2025-09-29 01:00:00号 做多(涨包跌) 入场=4031.84 出场=4029.84 出场时间=1759079700000 差价=-2.00 盈利=-4.96 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.417 | INFO | __main__:<module>:216 - 2025-09-29 01:45:00号 做多(涨包跌) 入场=4043.55 出场=4041.55 出场时间=1759082400000 差价=-2.00 盈利=-4.95 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.417 | INFO | __main__:<module>:216 - 2025-09-29 02:45:00号 做多(涨包跌) 入场=4038.12 出场=4036.12 出场时间=1759086000000 差价=-2.00 盈利=-4.95 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.417 | INFO | __main__:<module>:216 - 2025-09-29 03:15:00号 做空(跌包涨) 入场=4032.23 出场=4034.23 出场时间=1759087800000 差价=-2.00 盈利=-4.96 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.417 | INFO | __main__:<module>:216 - 2025-09-29 04:00:00号 做多(涨包跌) 入场=4037.42 出场=4035.42 出场时间=1759090500000 差价=-2.00 盈利=-4.95 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.417 | INFO | __main__:<module>:216 - 2025-09-29 04:30:00号 做多(涨包跌) 入场=4046.49 出场=4044.49 出场时间=1759095000000 差价=-2.00 盈利=-4.94 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.417 | INFO | __main__:<module>:216 - 2025-09-29 05:30:00号 做空(跌包涨) 入场=4048.44 出场=4050.44 出场时间=1759096800000 差价=-2.00 盈利=-4.94 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.417 | INFO | __main__:<module>:216 - 2025-09-29 07:45:00号 做多(涨包跌) 入场=4137.26 出场=4135.26 出场时间=1759104000000 差价=-2.00 盈利=-4.83 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.417 | INFO | __main__:<module>:216 - 2025-09-29 08:15:00号 做空(跌包涨) 入场=4134.85 出场=4136.85 出场时间=1759105800000 差价=-2.00 盈利=-4.84 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.417 | INFO | __main__:<module>:216 - 2025-09-29 08:45:00号 做空(跌包涨) 入场=4126.53 出场=4128.53 出场时间=1759107600000 差价=-2.00 盈利=-4.85 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.417 | INFO | __main__:<module>:216 - 2025-09-29 09:15:00号 做多(涨包跌) 入场=4131.72 出场=4129.72 出场时间=1759109400000 差价=-2.00 盈利=-4.84 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.418 | INFO | __main__:<module>:216 - 2025-09-29 09:30:00号 做空(跌包涨) 入场=4117.93 出场=4119.93 出场时间=1759110300000 差价=-2.00 盈利=-4.86 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.418 | INFO | __main__:<module>:216 - 2025-09-29 10:00:00号 做空(跌包涨) 入场=4112.65 出场=4114.65 出场时间=1759112100000 差价=-2.00 盈利=-4.86 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.418 | INFO | __main__:<module>:216 - 2025-09-29 10:45:00号 做空(跌包涨) 入场=4107.29 出场=4109.29 出场时间=1759114800000 差价=-2.00 盈利=-4.87 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.418 | INFO | __main__:<module>:216 - 2025-09-29 11:15:00号 做空(跌包涨) 入场=4102.41 出场=4104.41 出场时间=1759116600000 差价=-2.00 盈利=-4.88 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.418 | INFO | __main__:<module>:216 - 2025-09-29 12:00:00号 做多(涨包跌) 入场=4119.67 出场=4117.67 出场时间=1759119300000 差价=-2.00 盈利=-4.85 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.418 | INFO | __main__:<module>:216 - 2025-09-29 12:45:00号 做多(涨包跌) 入场=4118.75 出场=4116.75 出场时间=1759122000000 差价=-2.00 盈利=-4.86 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.418 | INFO | __main__:<module>:216 - 2025-09-29 13:45:00号 做多(涨包跌) 入场=4109.88 出场=4107.88 出场时间=1759125600000 差价=-2.00 盈利=-4.87 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-29 14:15:00号 做空(跌包涨) 入场=4097.98 出场=4099.98 出场时间=1759127400000 差价=-2.00 盈利=-4.88 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-29 15:00:00号 做空(跌包涨) 入场=4095.36 出场=4097.36 出场时间=1759130100000 差价=-2.00 盈利=-4.88 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-29 15:30:00号 做空(跌包涨) 入场=4092.53 出场=4094.53 出场时间=1759131900000 差价=-2.00 盈利=-4.89 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-29 15:45:00号 做多(涨包跌) 入场=4104.99 出场=4134.99 出场时间=1759137300000 差价=30.00 盈利=73.08 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-29 20:00:00号 做空(跌包涨) 入场=4100.87 出场=4102.87 出场时间=1759148100000 差价=-2.00 盈利=-4.88 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-29 20:15:00号 做多(涨包跌) 入场=4108.92 出场=4106.92 出场时间=1759149000000 差价=-2.00 盈利=-4.87 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-29 20:45:00号 做空(跌包涨) 入场=4102.06 出场=4104.06 出场时间=1759150800000 差价=-2.00 盈利=-4.88 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-29 21:15:00号 做多(涨包跌) 入场=4102.99 出场=4132.99 出场时间=1759152600000 差价=30.00 盈利=73.12 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-29 22:45:00号 做多(涨包跌) 入场=4189.88 出场=4187.88 出场时间=1759158000000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-30 00:30:00号 做空(跌包涨) 入场=4156.52 出场=4126.52 出场时间=1759165200000 差价=30.00 盈利=72.18 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-30 01:30:00号 做多(涨包跌) 入场=4145.04 出场=4143.04 出场时间=1759168800000 差价=-2.00 盈利=-4.83 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-30 02:00:00号 做多(涨包跌) 入场=4153.13 出场=4151.13 出场时间=1759169700000 差价=-2.00 盈利=-4.82 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-30 03:45:00号 做多(涨包跌) 入场=4180.31 出场=4210.31 出场时间=1759177800000 差价=30.00 盈利=71.77 开仓手续费=5u 平仓手续费=5.04
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-30 04:45:00号 做多(涨包跌) 入场=4229.13 出场=4227.13 出场时间=1759179600000 差价=-2.00 盈利=-4.73 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-30 07:00:00号 做多(涨包跌) 入场=4224.09 出场=4222.09 出场时间=1759187700000 差价=-2.00 盈利=-4.73 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.419 | INFO | __main__:<module>:216 - 2025-09-30 08:45:00号 做多(涨包跌) 入场=4216.80 出场=4214.80 出场时间=1759194000000 差价=-2.00 盈利=-4.74 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 10:15:00号 做空(跌包涨) 入场=4198.83 出场=4200.83 出场时间=1759199400000 差价=-2.00 盈利=-4.76 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 10:45:00号 做多(涨包跌) 入场=4201.68 出场=4199.68 出场时间=1759201200000 差价=-2.00 盈利=-4.76 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 11:30:00号 做多(涨包跌) 入场=4199.59 出场=4197.59 出场时间=1759203900000 差价=-2.00 盈利=-4.76 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 12:00:00号 做空(跌包涨) 入场=4196.16 出场=4198.16 出场时间=1759205700000 差价=-2.00 盈利=-4.77 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 14:00:00号 做多(涨包跌) 入场=4182.43 出场=4180.43 出场时间=1759217400000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 15:00:00号 做空(跌包涨) 入场=4188.05 出场=4190.05 出场时间=1759216500000 差价=-2.00 盈利=-4.78 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 17:30:00号 做多(涨包跌) 入场=4168.78 出场=4166.78 出场时间=1759225500000 差价=-2.00 盈利=-4.80 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 19:15:00号 做空(跌包涨) 入场=4153.02 出场=4155.02 出场时间=1759231800000 差价=-2.00 盈利=-4.82 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 19:30:00号 做多(涨包跌) 入场=4158.87 出场=4156.87 出场时间=1759233600000 差价=-2.00 盈利=-4.81 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 20:30:00号 做多(涨包跌) 入场=4165.40 出场=4163.40 出场时间=1759236300000 差价=-2.00 盈利=-4.80 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 21:00:00号 做空(跌包涨) 入场=4156.14 出场=4158.14 出场时间=1759238100000 差价=-2.00 盈利=-4.81 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 21:30:00号 做多(涨包跌) 入场=4164.44 出场=4162.44 出场时间=1759239900000 差价=-2.00 盈利=-4.80 开仓手续费=5u 平仓手续费=5.00
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 22:00:00号 做空(跌包涨) 入场=4152.29 出场=4122.29 出场时间=1759241700000 差价=30.00 盈利=72.25 开仓手续费=5u 平仓手续费=4.96
|
||||
2025-10-11 14:00:27.420 | INFO | __main__:<module>:216 - 2025-09-30 23:15:00号 做空(跌包涨) 入场=4115.98 出场=4117.98 出场时间=1759247100000 差价=-2.00 盈利=-4.86 开仓手续费=5u 平仓手续费=5.00
|
||||
""" # 这里放入你提供的完整日志数据
|
||||
|
||||
# 计算最大回撤
|
||||
results = calculate_max_drawdown(log_data)
|
||||
|
||||
# 打印结果
|
||||
print_analysis_results(results)
|
||||
|
||||
# 可选:绘制累计收益曲线(需要matplotlib)
|
||||
try:
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
plt.figure(figsize=(12, 6))
|
||||
plt.plot(results['cumulative_returns'], label='累计收益', linewidth=2)
|
||||
plt.axhline(y=0, color='r', linestyle='--', alpha=0.7, label='盈亏平衡线')
|
||||
|
||||
# 标记最大回撤区间
|
||||
start_idx, end_idx = results['max_drawdown_period']
|
||||
plt.axvspan(start_idx, end_idx, alpha=0.3, color='red', label='最大回撤区间')
|
||||
|
||||
plt.title('交易策略表现 - 累计收益曲线')
|
||||
plt.xlabel('交易次数')
|
||||
plt.ylabel('累计收益 (u)')
|
||||
plt.legend()
|
||||
plt.grid(True, alpha=0.3)
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
|
||||
except ImportError:
|
||||
print("如需可视化图表,请安装matplotlib: pip install matplotlib")
|
||||
327
weex/框架.py
Normal file
327
weex/框架.py
Normal file
@@ -0,0 +1,327 @@
|
||||
"""
|
||||
WEEX ETH-USDT 永续合约交易框架
|
||||
与 bitmart/框架.py 结构对应:浏览器 + API(K线/余额/持仓)+ 市价开平仓,供具体策略复用。
|
||||
"""
|
||||
import time
|
||||
from typing import Optional, Dict, List, Tuple
|
||||
|
||||
from tqdm import tqdm
|
||||
from loguru import logger
|
||||
from bit_tools import openBrowser
|
||||
from DrissionPage import ChromiumPage, ChromiumOptions
|
||||
from curl_cffi import requests
|
||||
|
||||
|
||||
# ==================== 配置 ====================
|
||||
class Config:
|
||||
TGE_URL = "http://127.0.0.1:50326"
|
||||
TGE_AUTHORIZATION = "Bearer asp_174003986c9b0799677c5b2c1adb76e402735d753bc91a91"
|
||||
|
||||
CONTRACT_ID = "10000002"
|
||||
PRODUCT_CODE = "cmt_ethusdt"
|
||||
KLINE_TYPE = "MINUTE_30"
|
||||
KLINE_LIMIT = 300
|
||||
TRADING_URL = "https://www.weex.com/zh-CN/futures/ETH-USDT"
|
||||
|
||||
MAX_RETRY_ATTEMPTS = 3
|
||||
RETRY_DELAY = 1
|
||||
|
||||
|
||||
# ==================== 主框架类 ====================
|
||||
class WeexFuturesTransaction:
|
||||
"""WEEX 永续合约交易框架:K线、余额、持仓、浏览器、市价开平仓"""
|
||||
|
||||
def __init__(self, tge_id):
|
||||
self.page: Optional[ChromiumPage] = None
|
||||
self.tge_port: Optional[int] = None
|
||||
self.tge_id = tge_id
|
||||
|
||||
self.session = requests.Session()
|
||||
self.headers: Optional[Dict] = None
|
||||
|
||||
self.start = 0 # 持仓: -1 空, 0 无, 1 多
|
||||
self.open_avg_price = None
|
||||
self.current_amount = None
|
||||
self.position_data: Optional[List] = None
|
||||
|
||||
self.pbar = tqdm(total=30, desc="等待K线", ncols=80)
|
||||
self.last_kline_time = None
|
||||
|
||||
# ------------------------- Token(请求 API 前需先取 token)-------------------------
|
||||
def _get_token(self) -> bool:
|
||||
"""从浏览器请求交易页时抓取 U-TOKEN,写入 session headers"""
|
||||
if not self.page:
|
||||
return False
|
||||
tab = self.page.new_tab()
|
||||
tab.listen.start("/user/security/getLanguageType")
|
||||
try:
|
||||
for attempt in range(Config.MAX_RETRY_ATTEMPTS):
|
||||
try:
|
||||
tab.get(url=Config.TRADING_URL)
|
||||
res = tab.listen.wait(timeout=5)
|
||||
if res.request.headers.get("U-TOKEN"):
|
||||
self.headers = dict(res.request.headers)
|
||||
self.session.headers.update(self.headers)
|
||||
logger.success("获取 token 成功")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.warning(f"获取 token 第 {attempt + 1} 次失败: {e}")
|
||||
if attempt < Config.MAX_RETRY_ATTEMPTS - 1:
|
||||
time.sleep(Config.RETRY_DELAY)
|
||||
return False
|
||||
finally:
|
||||
tab.close()
|
||||
|
||||
# ------------------------- API -------------------------
|
||||
def get_klines(
|
||||
self,
|
||||
contract_id: str = Config.CONTRACT_ID,
|
||||
product_code: str = Config.PRODUCT_CODE,
|
||||
kline_type: str = Config.KLINE_TYPE,
|
||||
limit: int = Config.KLINE_LIMIT,
|
||||
) -> Optional[List[Dict]]:
|
||||
"""获取 K 线,返回 [{'id', 'open', 'high', 'low', 'close'}, ...],按 id 升序。请求前需已取 token。"""
|
||||
# if not self._get_token():
|
||||
# logger.error("获取 token 失败,无法拉取 K 线")
|
||||
# return None
|
||||
params = {
|
||||
"contractId": contract_id,
|
||||
"productCode": product_code,
|
||||
"priceType": "LAST_PRICE",
|
||||
"klineType": kline_type,
|
||||
"limit": str(limit),
|
||||
"timeZone": "string",
|
||||
"languageType": "1",
|
||||
"sign": "SIGN",
|
||||
}
|
||||
for attempt in range(Config.MAX_RETRY_ATTEMPTS):
|
||||
try:
|
||||
response = self.session.get(
|
||||
"https://http-gateway2.elconvo.com/api/v1/public/quote/v1/getKlineV2",
|
||||
params=params,
|
||||
timeout=15,
|
||||
)
|
||||
if response.status_code != 200:
|
||||
if attempt < Config.MAX_RETRY_ATTEMPTS - 1:
|
||||
time.sleep(Config.RETRY_DELAY)
|
||||
continue
|
||||
data = response.json()
|
||||
if "data" not in data or "dataList" not in data["data"]:
|
||||
continue
|
||||
result = data["data"]["dataList"]
|
||||
kline_data = []
|
||||
for item in result:
|
||||
# [close, high, low, open, id]
|
||||
kline_data.append({
|
||||
"id": int(item[4]),
|
||||
"open": float(item[3]),
|
||||
"high": float(item[1]),
|
||||
"low": float(item[2]),
|
||||
"close": float(item[0]),
|
||||
})
|
||||
kline_data.sort(key=lambda x: x["id"])
|
||||
return kline_data
|
||||
except Exception as e:
|
||||
logger.error(f"获取 K 线异常: {e}")
|
||||
if attempt < Config.MAX_RETRY_ATTEMPTS - 1:
|
||||
time.sleep(Config.RETRY_DELAY)
|
||||
return None
|
||||
|
||||
def get_current_price(self) -> Optional[float]:
|
||||
"""用最近一根 K 线收盘价作为当前价"""
|
||||
klines = self.get_klines(limit=3)
|
||||
if klines:
|
||||
return float(klines[-1]["close"])
|
||||
return None
|
||||
|
||||
def get_available_balance(self) -> Optional[float]:
|
||||
"""合约账户可用余额"""
|
||||
if not self._get_token():
|
||||
return None
|
||||
for attempt in range(Config.MAX_RETRY_ATTEMPTS):
|
||||
try:
|
||||
response = self.session.post(
|
||||
"https://gateway2.ngsvsfx.cn/v1/gw/assetsWithBalance/new",
|
||||
timeout=15,
|
||||
)
|
||||
return float(response.json()["data"]["newContract"]["balanceList"][0]["available"])
|
||||
except Exception as e:
|
||||
logger.error(f"获取余额异常: {e}")
|
||||
if attempt < Config.MAX_RETRY_ATTEMPTS - 1:
|
||||
time.sleep(Config.RETRY_DELAY)
|
||||
return None
|
||||
|
||||
def get_position_status(self) -> bool:
|
||||
"""从成交历史解析持仓方向,更新 self.start (-1/0/1),成功返回 True"""
|
||||
# if not self._get_token():
|
||||
# return False
|
||||
payload = {
|
||||
"filterContractIdList": [10000002],
|
||||
"limit": 100,
|
||||
"languageType": 0,
|
||||
"sign": "SIGN",
|
||||
"timeZone": "string",
|
||||
}
|
||||
for attempt in range(Config.MAX_RETRY_ATTEMPTS):
|
||||
try:
|
||||
response = self.session.post(
|
||||
"https://http-gateway2.janapw.com/api/v1/private/order/v2/getHistoryOrderFillTransactionPage",
|
||||
json=payload,
|
||||
timeout=15,
|
||||
)
|
||||
datas = response.json().get("data", {}).get("dataList")
|
||||
self.position_data = datas or None
|
||||
if not datas:
|
||||
self.start = 0
|
||||
return True
|
||||
rev = list(datas)
|
||||
rev.reverse()
|
||||
start, start1 = 0, 0
|
||||
for i in rev:
|
||||
d = i.get("legacyOrderDirection")
|
||||
if d == "CLOSE_SHORT":
|
||||
start = 0
|
||||
elif d == "CLOSE_LONG":
|
||||
start1 = 0
|
||||
elif d == "OPEN_SHORT":
|
||||
start -= 1
|
||||
elif d == "OPEN_LONG":
|
||||
start1 += 1
|
||||
if start1:
|
||||
self.start = 1
|
||||
elif start:
|
||||
self.start = -1
|
||||
else:
|
||||
self.start = 0
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"获取持仓异常: {e}")
|
||||
if attempt < Config.MAX_RETRY_ATTEMPTS - 1:
|
||||
time.sleep(Config.RETRY_DELAY)
|
||||
return False
|
||||
|
||||
# ------------------------- 浏览器 -------------------------
|
||||
def openBrowser(self) -> bool:
|
||||
"""打开 TGE 对应浏览器实例"""
|
||||
try:
|
||||
bit_port = openBrowser(id=self.tge_id)
|
||||
co = ChromiumOptions()
|
||||
co.set_local_port(port=bit_port)
|
||||
self.page = ChromiumPage(addr_or_opts=co)
|
||||
self.tge_port = bit_port
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def take_over_browser(self) -> bool:
|
||||
"""接管已有浏览器"""
|
||||
if not self.tge_port:
|
||||
return False
|
||||
try:
|
||||
co = ChromiumOptions()
|
||||
co.set_local_port(self.tge_port)
|
||||
self.page = ChromiumPage(addr_or_opts=co)
|
||||
self.page.set.window.max()
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def close_extra_tabs(self) -> bool:
|
||||
"""关闭多余标签页,只保留第一个"""
|
||||
if not self.page:
|
||||
return False
|
||||
try:
|
||||
for idx, tab in enumerate(self.page.get_tabs()):
|
||||
if idx > 0:
|
||||
tab.close()
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def click_safe(self, xpath: str, sleep: float = 0.5) -> bool:
|
||||
"""安全点击"""
|
||||
try:
|
||||
ele = self.page.ele(xpath)
|
||||
if not ele:
|
||||
return False
|
||||
ele.scroll.to_see(center=True)
|
||||
time.sleep(sleep)
|
||||
ele.click(by_js=True)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
# ------------------------- 交易 -------------------------
|
||||
def 平仓(self) -> bool:
|
||||
"""市价平仓(闪电平仓)"""
|
||||
try:
|
||||
self.page.ele('x:(//span[normalize-space(text()) = "闪电平仓"])').scroll.to_see(center=True)
|
||||
time.sleep(0.5)
|
||||
self.page.ele('x:(//span[normalize-space(text()) = "闪电平仓"])').click(by_js=True)
|
||||
time.sleep(2)
|
||||
logger.info("执行平仓")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"平仓异常: {e}")
|
||||
return False
|
||||
|
||||
def 开单(self, marketPriceLongOrder: int = 0, size: Optional[float] = None) -> bool:
|
||||
"""
|
||||
市价开仓
|
||||
marketPriceLongOrder: 1 做多, -1 做空
|
||||
size: 数量(金额)
|
||||
"""
|
||||
if size is None or size <= 0:
|
||||
logger.warning("开单数量无效")
|
||||
return False
|
||||
try:
|
||||
self.click_safe('x:(//button[normalize-space(text()) = "市价"])')
|
||||
time.sleep(0.5)
|
||||
self.page.ele('x://input[@placeholder="请输入数量"]').input(size)
|
||||
time.sleep(0.5)
|
||||
if marketPriceLongOrder == -1:
|
||||
self.page.ele('x://*[normalize-space(text()) ="卖出开空"]').click(by_js=True)
|
||||
elif marketPriceLongOrder == 1:
|
||||
self.page.ele('x://*[normalize-space(text()) ="买入开多"]').click(by_js=True)
|
||||
else:
|
||||
return False
|
||||
logger.info(f"市价{'做空' if marketPriceLongOrder == -1 else '做多'} 数量={size}")
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"开单异常: {e}")
|
||||
return False
|
||||
|
||||
def ding(self, text: str, error: bool = False) -> None:
|
||||
"""日志/通知入口,可在此接入钉钉等"""
|
||||
if error:
|
||||
logger.error(text)
|
||||
else:
|
||||
logger.info(text)
|
||||
|
||||
def action(self) -> None:
|
||||
"""框架入口:打开浏览器 -> 交易页 -> 取 token -> 拉 K 线 -> 切市价(示例)"""
|
||||
if not self.openBrowser():
|
||||
self.ding("打开 TGE 失败!", error=True)
|
||||
return
|
||||
logger.info("TGE 浏览器已打开")
|
||||
|
||||
self.close_extra_tabs()
|
||||
self.page.get(url=Config.TRADING_URL)
|
||||
time.sleep(2)
|
||||
|
||||
if not self._get_token():
|
||||
self.ding("获取 token 失败", error=True)
|
||||
return
|
||||
|
||||
klines = self.get_klines()
|
||||
if klines:
|
||||
logger.info(f"获取到 {len(klines)} 根 K 线,最新收盘 {klines[-1]['close']}")
|
||||
|
||||
self.click_safe('x:(//button[normalize-space(text()) = "市价"])')
|
||||
# 此处可接具体策略循环:get_klines -> 信号 -> 开单/平仓
|
||||
# self.平仓()
|
||||
self.开单(marketPriceLongOrder=1, size=100)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
WeexFuturesTransaction(tge_id="86837a981aba4576be6916a0ef6ad785").action()
|
||||
@@ -1,237 +0,0 @@
|
||||
import datetime
|
||||
|
||||
from loguru import logger
|
||||
from models.weex import Weex15, Weex1
|
||||
|
||||
|
||||
def is_bullish(candle):
|
||||
"""判断是否是阳线(开盘价 < 收盘价,即涨)"""
|
||||
return float(candle['open']) < float(candle['close'])
|
||||
|
||||
|
||||
def is_bearish(candle):
|
||||
"""判断是否是阴线(开盘价 > 收盘价,即跌)"""
|
||||
return float(candle['open']) > float(candle['close'])
|
||||
|
||||
|
||||
def check_signal(prev, curr):
|
||||
"""
|
||||
判断是否出现包住形态,返回信号类型和方向:
|
||||
1. 前跌后涨包住 -> 做多信号 (bear_bull_engulf)
|
||||
2. 前涨后跌包住 -> 做空信号 (bull_bear_engulf)
|
||||
3. 前涨后涨包住 -> 做多信号 (bull_bull_engulf)
|
||||
4. 前跌后跌包住 -> 做空信号 (bear_bear_engulf)
|
||||
"""
|
||||
p_open, p_close = float(prev['open']), float(prev['close']) # 前一笔
|
||||
c_open, c_close = float(curr['open']), float(curr['close']) # 当前一笔
|
||||
|
||||
# 情况1:前跌后涨,且涨线包住前跌线 -> 做多信号
|
||||
if is_bullish(curr) and is_bearish(prev):
|
||||
if c_open <= p_close and c_close >= p_open:
|
||||
return "long", "bear_bull_engulf"
|
||||
|
||||
# # 情况2:前涨后跌,且跌线包住前涨线 -> 做空信号
|
||||
# if is_bearish(curr) and is_bullish(prev):
|
||||
# if c_open >= p_close and c_close <= p_open:
|
||||
# return "short", "bull_bear_engulf"
|
||||
|
||||
# # 情况3:前涨后涨,且后涨线包住前涨线 -> 做多信号
|
||||
# if is_bullish(curr) and is_bullish(prev):
|
||||
# if c_open <= p_open and c_close >= p_close:
|
||||
# return "long", "bull_bull_engulf"
|
||||
|
||||
# # 情况4:前跌后跌,且后跌线包住前跌线 -> 做空信号
|
||||
# if is_bearish(curr) and is_bearish(prev):
|
||||
# if c_open >= p_open and c_close <= p_close:
|
||||
# return "short", "bear_bear_engulf"
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
def simulate_trade(direction, entry_price, future_candles, take_profit_diff=30, stop_loss_diff=-10):
|
||||
"""
|
||||
模拟交易(逐根K线回测)
|
||||
改进版:考虑开盘跳空触发止盈/止损的情况
|
||||
"""
|
||||
for candle in future_candles:
|
||||
open_p = float(candle['open'])
|
||||
high = float(candle['high'])
|
||||
low = float(candle['low'])
|
||||
close = float(candle['close'])
|
||||
|
||||
if direction == "long":
|
||||
tp_price = entry_price + take_profit_diff
|
||||
sl_price = entry_price + stop_loss_diff
|
||||
|
||||
# 🧩 开盘就跳空止盈
|
||||
if open_p >= tp_price:
|
||||
return open_p, open_p - entry_price, candle['id']
|
||||
|
||||
# 🧩 开盘就跳空止损
|
||||
if open_p <= sl_price:
|
||||
return open_p, open_p - entry_price, candle['id']
|
||||
|
||||
# 正常区间内触发止盈/止损
|
||||
if high >= tp_price:
|
||||
return tp_price, take_profit_diff, candle['id']
|
||||
if low <= sl_price:
|
||||
return sl_price, stop_loss_diff, candle['id']
|
||||
|
||||
elif direction == "short":
|
||||
tp_price = entry_price - take_profit_diff
|
||||
sl_price = entry_price - stop_loss_diff
|
||||
|
||||
# 🧩 开盘就跳空止盈
|
||||
if open_p <= tp_price:
|
||||
return open_p, entry_price - open_p, candle['id']
|
||||
|
||||
# 🧩 开盘就跳空止损
|
||||
if open_p >= sl_price:
|
||||
return open_p, entry_price - open_p, candle['id']
|
||||
|
||||
# 正常区间内触发止盈/止损
|
||||
if low <= tp_price:
|
||||
return tp_price, take_profit_diff, candle['id']
|
||||
if high >= sl_price:
|
||||
return sl_price, stop_loss_diff, candle['id']
|
||||
|
||||
# 未触发止盈止损,按最后收盘价平仓
|
||||
final_price = float(future_candles[-1]['close'])
|
||||
if direction == "long":
|
||||
diff_money = final_price - entry_price
|
||||
else:
|
||||
diff_money = entry_price - final_price
|
||||
|
||||
return final_price, diff_money, future_candles[-1]['id']
|
||||
|
||||
|
||||
def get_data_by_date(date_str):
|
||||
# 将日期字符串转换为 datetime 对象
|
||||
try:
|
||||
target_date = datetime.datetime.strptime(date_str, '%Y-%m-%d')
|
||||
except ValueError:
|
||||
print("日期格式不正确,请使用 YYYY-MM-DD 格式。")
|
||||
return []
|
||||
|
||||
# 计算该天的开始时间戳(毫秒级)
|
||||
start_timestamp = int(target_date.timestamp() * 1000)
|
||||
# 计算该天的结束时间戳(毫秒级),即下一天 00:00:00 的时间戳减去 1 毫秒
|
||||
end_timestamp = int((target_date + datetime.timedelta(days=1)).timestamp() * 1000) - 1
|
||||
|
||||
# 查询该天的数据,并按照 id 字段从小到大排序
|
||||
query = Weex1.select().where(Weex1.id.between(start_timestamp, end_timestamp)).order_by(Weex1.id.asc())
|
||||
results = list(query)
|
||||
|
||||
# 将结果转换为列表嵌套字典的形式
|
||||
data_list = []
|
||||
for item in results:
|
||||
item_dict = {
|
||||
'id': item.id,
|
||||
'open': item.open,
|
||||
'high': item.high,
|
||||
'low': item.low,
|
||||
'close': item.close
|
||||
}
|
||||
data_list.append(item_dict)
|
||||
|
||||
return data_list
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
# 示例调用
|
||||
datas = []
|
||||
for i in range(1, 31):
|
||||
date_str = f'2025-9-{i}'
|
||||
data = get_data_by_date(date_str)
|
||||
|
||||
datas.extend(data)
|
||||
|
||||
datas = sorted(datas, key=lambda x: x["id"])
|
||||
|
||||
for i in range(1, 51):
|
||||
for i1 in range(1, 51):
|
||||
|
||||
zh_project = 0 # 累计盈亏
|
||||
all_trades = [] # 保存所有交易明细
|
||||
daily_signals = 0 # 信号总数
|
||||
daily_wins = 0
|
||||
daily_profit = 0 # 价差总和
|
||||
|
||||
# 四种信号类型的统计
|
||||
signal_stats = {
|
||||
"bear_bull_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "涨包跌"},
|
||||
"bull_bear_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "跌包涨"},
|
||||
# "bull_bull_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "涨包涨"},
|
||||
# "bear_bear_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "跌包跌"}
|
||||
}
|
||||
|
||||
# 遍历每根K线,寻找信号
|
||||
for idx in range(1, len(datas) - 1): # 留出未来K线
|
||||
prev, curr = datas[idx - 1], datas[idx] # 前一笔,当前一笔
|
||||
entry_candle = datas[idx + 1] # 下一根开盘价作为入场价
|
||||
future_candles = datas[idx + 1:] # 未来行情
|
||||
|
||||
entry_open = float(entry_candle['open']) # 开仓价格
|
||||
direction, signal_type = check_signal(prev, curr) # 判断开仓方向和信号类型
|
||||
|
||||
if direction and signal_type:
|
||||
daily_signals += 1
|
||||
|
||||
exit_price, diff, exit_time = simulate_trade(
|
||||
direction,
|
||||
entry_open,
|
||||
future_candles,
|
||||
take_profit_diff=i1,
|
||||
stop_loss_diff=-i
|
||||
)
|
||||
|
||||
# 统计该信号类型的表现
|
||||
signal_stats[signal_type]["count"] += 1
|
||||
signal_stats[signal_type]["total_profit"] += diff
|
||||
if diff > 0:
|
||||
signal_stats[signal_type]["wins"] += 1
|
||||
daily_wins += 1
|
||||
|
||||
daily_profit += diff
|
||||
|
||||
# 将时间戳转换为本地时间
|
||||
local_time = datetime.datetime.fromtimestamp(entry_candle['id'] / 1000)
|
||||
formatted_time = local_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
exit_time = datetime.datetime.fromtimestamp(exit_time / 1000)
|
||||
exit_time1 = exit_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
# 保存交易详情
|
||||
all_trades.append(
|
||||
(
|
||||
f"{formatted_time}号",
|
||||
"做多" if direction == "long" else "做空",
|
||||
signal_stats[signal_type]["name"],
|
||||
entry_open,
|
||||
exit_price,
|
||||
diff,
|
||||
exit_time1
|
||||
)
|
||||
)
|
||||
|
||||
# ===== 输出每笔交易详情 =====
|
||||
logger.info("===== 每笔交易详情 =====")
|
||||
logger.info(f"{i},{i1}")
|
||||
n = n1 = 0 # n = 总盈利,n1 = 总手续费
|
||||
for date, direction, signal_name, entry, exit, diff, end_time in all_trades:
|
||||
profit_amount = diff / entry * 10000 # 计算盈利金额
|
||||
close_fee = 10000 / entry * exit * 0.0005 # 平仓手续费
|
||||
|
||||
# logger.info(
|
||||
# f"{date} {direction}({signal_name}) 入场={entry:.2f} 出场={exit:.2f} 出场时间={end_time} "
|
||||
# f"差价={diff:.2f} 盈利={profit_amount:.2f} "
|
||||
# f"开仓手续费=5u 平仓手续费={close_fee:.2f}"
|
||||
# )
|
||||
n1 += 5 + close_fee
|
||||
n += profit_amount
|
||||
|
||||
if n > n1 * 0.1:
|
||||
print(f'一共笔数:{len(all_trades)}')
|
||||
print(f"一共盈利:{n:.2f}")
|
||||
print(f'一共手续费:{n1:.2f}')
|
||||
@@ -1,331 +0,0 @@
|
||||
"""
|
||||
量化交易回测系统 - 策略方向反转版
|
||||
功能:基于包住形态的交易信号识别和回测分析(信号方向已按用户要求反转)
|
||||
作者:量化交易团队(修改:ChatGPT)
|
||||
版本:2.1
|
||||
"""
|
||||
|
||||
import datetime
|
||||
from typing import List, Dict, Tuple, Optional, Any
|
||||
from dataclasses import dataclass
|
||||
from loguru import logger
|
||||
from peewee import fn
|
||||
from models.weex import Weex15, Weex1
|
||||
|
||||
|
||||
# ===============================================================
|
||||
# 📊 配置管理类
|
||||
# ===============================================================
|
||||
|
||||
@dataclass
|
||||
class BacktestConfig:
|
||||
"""回测配置类"""
|
||||
# 交易参数
|
||||
take_profit: float = 8.0 # 止盈点数
|
||||
stop_loss: float = -1.0 # 止损点数(负数表示点差方向)
|
||||
contract_size: float = 10000 # 合约规模
|
||||
open_fee: float = 5.0 # 开仓手续费
|
||||
close_fee_rate: float = 0.0005 # 平仓手续费率
|
||||
|
||||
# 回测日期范围
|
||||
start_date: str = "2025-07-01"
|
||||
end_date: str = "2025-07-31"
|
||||
|
||||
# 信号参数
|
||||
enable_bear_bull_engulf: bool = True # 涨包跌(命名保留)
|
||||
enable_bull_bear_engulf: bool = True # 跌包涨(命名保留)
|
||||
|
||||
def __post_init__(self):
|
||||
"""验证配置参数"""
|
||||
if self.take_profit <= 0:
|
||||
raise ValueError("止盈点数必须大于0")
|
||||
if self.stop_loss >= 0:
|
||||
raise ValueError("止损点数必须小于0")
|
||||
|
||||
|
||||
@dataclass
|
||||
class TradeRecord:
|
||||
"""交易记录类"""
|
||||
entry_time: datetime.datetime
|
||||
exit_time: datetime.datetime
|
||||
signal_type: str
|
||||
direction: str
|
||||
entry_price: float
|
||||
exit_price: float
|
||||
profit_loss: float
|
||||
profit_amount: float
|
||||
total_fee: float
|
||||
net_profit: float
|
||||
|
||||
|
||||
@dataclass
|
||||
class SignalStats:
|
||||
"""信号统计类"""
|
||||
signal_name: str
|
||||
count: int = 0
|
||||
wins: int = 0
|
||||
total_profit: float = 0.0
|
||||
|
||||
@property
|
||||
def win_rate(self) -> float:
|
||||
"""胜率计算"""
|
||||
return (self.wins / self.count * 100) if self.count > 0 else 0.0
|
||||
|
||||
@property
|
||||
def avg_profit(self) -> float:
|
||||
"""平均盈利"""
|
||||
return self.total_profit / self.count if self.count > 0 else 0.0
|
||||
|
||||
|
||||
# ===============================================================
|
||||
# 📊 数据获取模块
|
||||
# ===============================================================
|
||||
|
||||
def get_data_by_date(model, date_str):
|
||||
"""按天获取指定表的数据,date_str 需为 YYYY-MM-DD"""
|
||||
try:
|
||||
target_date = datetime.datetime.strptime(date_str, '%Y-%m-%d')
|
||||
except ValueError:
|
||||
logger.error("日期格式不正确,请使用 YYYY-MM-DD 格式。")
|
||||
return []
|
||||
|
||||
start_ts = int(target_date.timestamp() * 1000)
|
||||
end_ts = int((target_date + datetime.timedelta(days=1)).timestamp() * 1000) - 1
|
||||
|
||||
query = (model
|
||||
.select()
|
||||
.where(model.id.between(start_ts, end_ts))
|
||||
.order_by(model.id.asc()))
|
||||
|
||||
return [
|
||||
{'id': i.id, 'open': i.open, 'high': i.high, 'low': i.low, 'close': i.close}
|
||||
for i in query
|
||||
]
|
||||
|
||||
|
||||
def get_future_data_1min(start_ts, end_ts):
|
||||
"""获取指定时间范围内的 1 分钟数据"""
|
||||
query = (Weex1
|
||||
.select()
|
||||
.where(Weex1.id.between(start_ts, end_ts))
|
||||
.order_by(Weex1.id.asc()))
|
||||
return [{'id': i.id, 'open': i.open, 'high': i.high, 'low': i.low, 'close': i.close} for i in query]
|
||||
|
||||
|
||||
# ===============================================================
|
||||
# 📈 信号判定模块(**方向已反转**)
|
||||
# ===============================================================
|
||||
|
||||
def is_bullish(c): return float(c['open']) < float(c['close'])
|
||||
|
||||
|
||||
def is_bearish(c): return float(c['open']) > float(c['close'])
|
||||
|
||||
|
||||
def check_signal(prev, curr):
|
||||
"""
|
||||
判断是否出现包住形态,并返回(direction, signal_key)
|
||||
注意:根据用户要求,这里把原来的方向映射“反转”:
|
||||
- 如果是 前跌后涨包住(过去我们认为做多),现在**改为做空**
|
||||
- 如果是 前涨后跌包住(过去我们认为做空),现在**改为做多**
|
||||
signal_key 保持 "bear_bull_engulf" / "bull_bear_engulf" 以便统计区分信号种类
|
||||
"""
|
||||
p_open, p_close = float(prev['open']), float(prev['close'])
|
||||
c_open, c_close = float(curr['open']), float(curr['close'])
|
||||
|
||||
# 前跌后涨包住 -> **原来做多,现在改为做空**
|
||||
if is_bullish(curr) and is_bearish(prev) and c_open <= p_close and c_close >= p_open:
|
||||
return "short", "bear_bull_engulf"
|
||||
|
||||
# 前涨后跌包住 -> **原来做空,现在改为做多**
|
||||
if is_bearish(curr) and is_bullish(prev) and c_open >= p_close and c_close <= p_open:
|
||||
return "long", "bull_bear_engulf"
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
# ===============================================================
|
||||
# 💹 回测模拟模块(使用 1 分钟数据)
|
||||
# ===============================================================
|
||||
|
||||
def simulate_trade(direction, entry_price, entry_time, next_15min_time, tp=8, sl=-1):
|
||||
"""
|
||||
用 1 分钟数据进行精细化止盈止损模拟
|
||||
entry_time: 当前信号的 entry candle id(毫秒时间戳)
|
||||
next_15min_time: 下一个15min时间戳,用于界定止盈止损分析范围
|
||||
direction:'long' 或 'short'
|
||||
entry_price:开仓价格
|
||||
"""
|
||||
# 查 15 分钟之间的 1 分钟数据
|
||||
future_candles = get_future_data_1min(entry_time, next_15min_time)
|
||||
if not future_candles:
|
||||
return None, 0, None
|
||||
|
||||
# 止盈止损价格(tp 为正,sl 为负)
|
||||
tp_price = entry_price + tp if direction == "long" else entry_price - tp
|
||||
sl_price = entry_price + sl if direction == "long" else entry_price - sl
|
||||
|
||||
for candle in future_candles:
|
||||
open_p, high, low = map(float, (candle['open'], candle['high'], candle['low']))
|
||||
|
||||
if direction == "long":
|
||||
# 开盘跳空优先
|
||||
if open_p >= tp_price:
|
||||
return open_p, open_p - entry_price, candle['id']
|
||||
if open_p <= sl_price:
|
||||
return open_p, open_p - entry_price, candle['id']
|
||||
# 盘中触及
|
||||
if high >= tp_price:
|
||||
return tp_price, tp, candle['id']
|
||||
if low <= sl_price:
|
||||
return sl_price, sl, candle['id']
|
||||
|
||||
else: # short
|
||||
if open_p <= tp_price:
|
||||
return open_p, entry_price - open_p, candle['id']
|
||||
if open_p >= sl_price:
|
||||
return open_p, entry_price - open_p, candle['id']
|
||||
if low <= tp_price:
|
||||
return tp_price, tp, candle['id']
|
||||
if high >= sl_price:
|
||||
return sl_price, sl, candle['id']
|
||||
|
||||
# 未触及 TP/SL,用最后一根收盘价平仓
|
||||
final = future_candles[-1]
|
||||
final_price = float(final['close'])
|
||||
diff = (final_price - entry_price) if direction == "long" else (entry_price - final_price)
|
||||
return final_price, diff, final['id']
|
||||
|
||||
|
||||
# ===============================================================
|
||||
# 📊 主回测流程(含单笔持仓逻辑)
|
||||
# ===============================================================
|
||||
|
||||
def backtest_single_position(dates: List[str], tp: float, sl: float):
|
||||
"""单笔持仓回测,保证任意时刻只有一笔持仓"""
|
||||
all_data = []
|
||||
for date_str in dates:
|
||||
all_data.extend(get_data_by_date(Weex15, date_str))
|
||||
all_data.sort(key=lambda x: x['id'])
|
||||
|
||||
stats = {
|
||||
"bear_bull_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "涨包跌(bear_bull_engulf)"},
|
||||
"bull_bear_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "跌包涨(bull_bear_engulf)"},
|
||||
}
|
||||
|
||||
trades = []
|
||||
current_position = None # 当前持仓信息(字典或 None)
|
||||
|
||||
for idx in range(1, len(all_data) - 1):
|
||||
prev, curr = all_data[idx - 1], all_data[idx]
|
||||
entry_candle = all_data[idx + 1]
|
||||
|
||||
direction, signal = check_signal(prev, curr)
|
||||
if not direction:
|
||||
continue
|
||||
|
||||
# 下一个 15 分钟K线的时间范围(若长度不足则取到最后)
|
||||
next_15min_time = all_data[idx + 50]['id'] if idx + 50 < len(all_data) else all_data[-1]['id']
|
||||
entry_price = float(entry_candle['open'])
|
||||
|
||||
# 如果有持仓
|
||||
if current_position:
|
||||
# 同向信号 -> 跳过不开(继续等待当前持仓结束)
|
||||
if current_position['direction'] == direction:
|
||||
continue
|
||||
# 反向信号 -> 先按当前持仓的规则用 simulate_trade 平仓,然后再开新仓
|
||||
else:
|
||||
exit_price, diff, exit_time = simulate_trade(
|
||||
current_position['direction'],
|
||||
current_position['entry_price'],
|
||||
current_position['entry_time'],
|
||||
entry_candle['id'],
|
||||
tp=tp,
|
||||
sl=sl
|
||||
)
|
||||
if exit_price is not None:
|
||||
# 记录平仓交易(一笔持仓作为一条记录)
|
||||
trades.append({
|
||||
"entry_time": datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
"exit_time": datetime.datetime.fromtimestamp(exit_time / 1000),
|
||||
"signal": current_position['signal'],
|
||||
"direction": "做多" if current_position['direction'] == "long" else "做空",
|
||||
"entry": current_position['entry_price'],
|
||||
"exit": exit_price,
|
||||
"diff": diff
|
||||
})
|
||||
# 更新统计(signal 字段是中文名)
|
||||
stats_key = 'bear_bull_engulf' if current_position['signal'].startswith('涨包跌') else 'bull_bear_engulf'
|
||||
stats[stats_key]['count'] += 1
|
||||
stats[stats_key]['total_profit'] += diff
|
||||
if diff > 0:
|
||||
stats[stats_key]['wins'] += 1
|
||||
|
||||
# 清空当前持仓,接下来在下方开新仓
|
||||
current_position = None
|
||||
|
||||
# 开新仓(无论之前是否有仓位,若有仓位已被平掉现在可以开仓)
|
||||
current_position = {
|
||||
"direction": direction,
|
||||
"signal": stats[signal]['name'],
|
||||
"entry_price": entry_price,
|
||||
"entry_time": entry_candle['id']
|
||||
}
|
||||
|
||||
# 最后一笔持仓如果未平仓,用最后收盘价平掉(以全数据范围最后时间为结算界限)
|
||||
if current_position:
|
||||
exit_price, diff, exit_time = simulate_trade(
|
||||
current_position['direction'],
|
||||
current_position['entry_price'],
|
||||
current_position['entry_time'],
|
||||
all_data[-1]['id'],
|
||||
tp=tp,
|
||||
sl=sl
|
||||
)
|
||||
if exit_price is not None:
|
||||
trades.append({
|
||||
"entry_time": datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
"exit_time": datetime.datetime.fromtimestamp(exit_time / 1000),
|
||||
"signal": current_position['signal'],
|
||||
"direction": "做多" if current_position['direction'] == "long" else "做空",
|
||||
"entry": current_position['entry_price'],
|
||||
"exit": exit_price,
|
||||
"diff": diff
|
||||
})
|
||||
stats_key = 'bear_bull_engulf' if current_position['signal'].startswith('涨包跌') else 'bull_bear_engulf'
|
||||
stats[stats_key]['count'] += 1
|
||||
stats[stats_key]['total_profit'] += diff
|
||||
if diff > 0:
|
||||
stats[stats_key]['wins'] += 1
|
||||
|
||||
return trades, stats
|
||||
|
||||
|
||||
# ===============================================================
|
||||
# 🚀 启动主流程(示例)
|
||||
# ===============================================================
|
||||
if __name__ == '__main__':
|
||||
# 修正日期格式为 YYYY-MM-DD(零填充)
|
||||
dates = [f"2025-09-{i:02d}" for i in range(1, 31)]
|
||||
|
||||
# 示例参数:止盈 10 点,止损 -50 点(保留你的原调用方式)
|
||||
trades, stats = backtest_single_position(dates, tp=50, sl=-10)
|
||||
|
||||
logger.info("===== 每笔交易详情 =====")
|
||||
for t in trades:
|
||||
logger.info(
|
||||
f"{t['entry_time']} {t['direction']}({t['signal']}) "
|
||||
f"入场={t['entry']:.2f} 出场={t['exit']:.2f} 出场时间={t['exit_time']} "
|
||||
f"差价={t['diff']:.2f}"
|
||||
)
|
||||
|
||||
total_profit = sum(t['diff'] / t['entry'] * 10000 for t in trades) if trades else 0.0
|
||||
total_fee = sum(5 + 10000 / t['entry'] * t['exit'] * 0.0005 for t in trades) if trades else 0.0
|
||||
|
||||
print(f"\n一共交易笔数:{len(trades)}")
|
||||
print(f"一共盈利(按手数/点值换算示例):{total_profit:.2f}")
|
||||
print(f"一共手续费:{total_fee:.2f}")
|
||||
print(f"净利润:{total_profit - total_fee:.2f}")
|
||||
print("\n===== 信号统计 =====")
|
||||
for k, v in stats.items():
|
||||
print(f"{v['name']} -> 笔数: {v['count']}, 胜数: {v['wins']}, 总点差: {v['total_profit']:.2f}")
|
||||
@@ -1,409 +0,0 @@
|
||||
"""
|
||||
量化交易回测系统 - 优化版
|
||||
功能:基于包住形态的交易信号识别和回测分析
|
||||
作者:量化交易团队
|
||||
版本:2.0
|
||||
"""
|
||||
|
||||
import datetime
|
||||
from typing import List, Dict, Tuple, Optional, Any
|
||||
from dataclasses import dataclass
|
||||
from loguru import logger
|
||||
from peewee import fn
|
||||
from models.weex import Weex15, Weex1
|
||||
|
||||
|
||||
# ===============================================================
|
||||
# 📊 配置管理类
|
||||
# ===============================================================
|
||||
|
||||
@dataclass
|
||||
class BacktestConfig:
|
||||
"""回测配置类"""
|
||||
# 交易参数
|
||||
take_profit: float = 8.0 # 止盈点数
|
||||
stop_loss: float = -1.0 # 止损点数
|
||||
contract_size: float = 10000 # 合约规模
|
||||
open_fee: float = 5.0 # 开仓手续费
|
||||
close_fee_rate: float = 0.0005 # 平仓手续费率
|
||||
|
||||
# 回测日期范围
|
||||
start_date: str = "2025-7-1"
|
||||
end_date: str = "2025-7-31"
|
||||
|
||||
# 信号参数
|
||||
enable_bear_bull_engulf: bool = True # 涨包跌信号
|
||||
enable_bull_bear_engulf: bool = True # 跌包涨信号
|
||||
|
||||
def __post_init__(self):
|
||||
"""验证配置参数"""
|
||||
if self.take_profit <= 0:
|
||||
raise ValueError("止盈点数必须大于0")
|
||||
if self.stop_loss >= 0:
|
||||
raise ValueError("止损点数必须小于0")
|
||||
|
||||
|
||||
@dataclass
|
||||
class TradeRecord:
|
||||
"""交易记录类"""
|
||||
entry_time: datetime.datetime
|
||||
exit_time: datetime.datetime
|
||||
signal_type: str
|
||||
direction: str
|
||||
entry_price: float
|
||||
exit_price: float
|
||||
profit_loss: float
|
||||
profit_amount: float
|
||||
total_fee: float
|
||||
net_profit: float
|
||||
|
||||
|
||||
@dataclass
|
||||
class SignalStats:
|
||||
"""信号统计类"""
|
||||
signal_name: str
|
||||
count: int = 0
|
||||
wins: int = 0
|
||||
total_profit: float = 0.0
|
||||
|
||||
@property
|
||||
def win_rate(self) -> float:
|
||||
"""胜率计算"""
|
||||
return (self.wins / self.count * 100) if self.count > 0 else 0.0
|
||||
|
||||
@property
|
||||
def avg_profit(self) -> float:
|
||||
"""平均盈利"""
|
||||
return self.total_profit / self.count if self.count > 0 else 0.0
|
||||
|
||||
|
||||
# ===============================================================
|
||||
# 📊 数据获取模块
|
||||
# ===============================================================
|
||||
|
||||
def get_data_by_date(model, date_str):
|
||||
"""按天获取指定表的数据"""
|
||||
try:
|
||||
target_date = datetime.datetime.strptime(date_str, '%Y-%m-%d')
|
||||
except ValueError:
|
||||
logger.error("日期格式不正确,请使用 YYYY-MM-DD 格式。")
|
||||
return []
|
||||
|
||||
start_ts = int(target_date.timestamp() * 1000)
|
||||
end_ts = int((target_date + datetime.timedelta(days=1)).timestamp() * 1000) - 1
|
||||
|
||||
query = (model
|
||||
.select()
|
||||
.where(model.id.between(start_ts, end_ts))
|
||||
.order_by(model.id.asc()))
|
||||
|
||||
return [
|
||||
{'id': i.id, 'open': i.open, 'high': i.high, 'low': i.low, 'close': i.close}
|
||||
for i in query
|
||||
]
|
||||
|
||||
|
||||
def get_future_data_1min(start_ts, end_ts):
|
||||
"""获取指定时间范围内的 1 分钟数据"""
|
||||
query = (Weex1
|
||||
.select()
|
||||
.where(Weex1.id.between(start_ts, end_ts))
|
||||
.order_by(Weex1.id.asc()))
|
||||
return [{'id': i.id, 'open': i.open, 'high': i.high, 'low': i.low, 'close': i.close} for i in query]
|
||||
|
||||
|
||||
# ===============================================================
|
||||
# 📈 信号判定模块
|
||||
# ===============================================================
|
||||
|
||||
def is_bullish(c): return float(c['open']) < float(c['close'])
|
||||
|
||||
|
||||
def is_bearish(c): return float(c['open']) > float(c['close'])
|
||||
|
||||
|
||||
def check_signal(prev, curr):
|
||||
"""判断是否出现包住形态"""
|
||||
p_open, p_close = float(prev['open']), float(prev['close'])
|
||||
c_open, c_close = float(curr['open']), float(curr['close'])
|
||||
|
||||
# 前跌后涨包住 -> 做多
|
||||
if is_bullish(curr) and is_bearish(prev) and c_open <= p_close and c_close >= p_open:
|
||||
return "long", "bear_bull_engulf"
|
||||
|
||||
# 前涨后跌包住 -> 做空
|
||||
if is_bearish(curr) and is_bullish(prev) and c_open >= p_close and c_close <= p_open:
|
||||
return "short", "bull_bear_engulf"
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
# ===============================================================
|
||||
# 💹 回测模拟模块(使用 1 分钟数据)
|
||||
# ===============================================================
|
||||
|
||||
def simulate_trade(direction, entry_price, entry_time, next_15min_time, tp=8, sl=-1):
|
||||
"""
|
||||
用 1 分钟数据进行精细化止盈止损模拟
|
||||
entry_time: 当前信号的 entry candle id(毫秒时间戳)
|
||||
next_15min_time: 下一个15min时间戳,用于界定止盈止损分析范围
|
||||
|
||||
direction:信号类型
|
||||
entry_price:开仓价格
|
||||
entry_time:开仓时间
|
||||
next_15min_time:15分钟未来行情
|
||||
|
||||
"""
|
||||
# 查 15 分钟之间的 1 分钟数据
|
||||
future_candles = get_future_data_1min(entry_time, next_15min_time)
|
||||
if not future_candles:
|
||||
return None, 0, None
|
||||
|
||||
tp_price = entry_price + tp if direction == "long" else entry_price - tp # 止盈价位
|
||||
sl_price = entry_price + sl if direction == "long" else entry_price - sl # 止损价位
|
||||
|
||||
for candle in future_candles:
|
||||
open_p, high, low = map(float, (candle['open'], candle['high'], candle['low']))
|
||||
|
||||
if direction == "long": # long
|
||||
if open_p >= tp_price: # 开盘跳空止盈 涨信号,
|
||||
return open_p, open_p - entry_price, candle['id']
|
||||
if open_p <= sl_price: # 开盘跳空止损
|
||||
return open_p, open_p - entry_price, candle['id']
|
||||
if high >= tp_price:
|
||||
return tp_price, tp, candle['id']
|
||||
if low <= sl_price:
|
||||
return sl_price, sl, candle['id']
|
||||
|
||||
else: # short 跌信号
|
||||
if open_p <= tp_price: #
|
||||
return open_p, entry_price - open_p, candle['id']
|
||||
if open_p >= sl_price:
|
||||
return open_p, entry_price - open_p, candle['id']
|
||||
if low <= tp_price:
|
||||
return tp_price, tp, candle['id']
|
||||
if high >= sl_price:
|
||||
return sl_price, sl, candle['id']
|
||||
|
||||
# 未触发止盈止损,用最后一根收盘价平仓
|
||||
final = future_candles[-1]
|
||||
final_price = float(final['close'])
|
||||
diff = (final_price - entry_price) if direction == "long" else (entry_price - final_price)
|
||||
return final_price, diff, final['id']
|
||||
|
||||
|
||||
# ===============================================================
|
||||
# 📊 主回测流程
|
||||
# ===============================================================
|
||||
|
||||
def backtest(dates, tp, sl):
|
||||
"""
|
||||
datas:日期的列表
|
||||
|
||||
:param dates:
|
||||
:param tp:
|
||||
:param sl:
|
||||
:return:
|
||||
"""
|
||||
|
||||
all_data = []
|
||||
for date_str in dates:
|
||||
all_data.extend(get_data_by_date(Weex15, date_str)) # 获取每天的数据,15分钟k线数据
|
||||
|
||||
all_data.sort(key=lambda x: x['id'])
|
||||
|
||||
stats = {
|
||||
"bear_bull_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "涨包跌"},
|
||||
"bull_bear_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "跌包涨"},
|
||||
}
|
||||
|
||||
trades = []
|
||||
|
||||
for idx in range(1, len(all_data) - 1):
|
||||
prev, curr = all_data[idx - 1], all_data[idx] # 前一笔,当前一笔
|
||||
entry_candle = all_data[idx + 1] # 下一笔开仓k线
|
||||
|
||||
direction, signal = check_signal(prev, curr)
|
||||
if not direction:
|
||||
continue
|
||||
|
||||
# 下一个 15 分钟K线的时间范围
|
||||
next_15min_time = all_data[idx + 50]['id'] if idx + 50 < len(all_data) else all_data[-1]['id']
|
||||
|
||||
entry_price = float(entry_candle['open']) # 开仓价格
|
||||
exit_price, diff, exit_time = simulate_trade(
|
||||
direction,
|
||||
entry_price,
|
||||
entry_candle['id'],
|
||||
next_15min_time,
|
||||
tp=tp,
|
||||
sl=sl
|
||||
)
|
||||
|
||||
if exit_price is None:
|
||||
continue
|
||||
|
||||
stats[signal]['count'] += 1
|
||||
stats[signal]['total_profit'] += diff
|
||||
if diff > 0:
|
||||
stats[signal]['wins'] += 1
|
||||
|
||||
trades.append({
|
||||
"entry_time": datetime.datetime.fromtimestamp(entry_candle['id'] / 1000),
|
||||
"exit_time": datetime.datetime.fromtimestamp(exit_time / 1000),
|
||||
"signal": stats[signal]['name'],
|
||||
"direction": "做多" if direction == "long" else "做空",
|
||||
"entry": entry_price,
|
||||
"exit": exit_price,
|
||||
"diff": diff
|
||||
})
|
||||
|
||||
return trades, stats
|
||||
|
||||
|
||||
def backtest_single_position(dates, tp, sl):
|
||||
"""单笔持仓回测,处理同向/反向信号"""
|
||||
all_data = []
|
||||
for date_str in dates:
|
||||
all_data.extend(get_data_by_date(Weex15, date_str))
|
||||
all_data.sort(key=lambda x: x['id'])
|
||||
|
||||
stats = {
|
||||
"bear_bull_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "涨包跌"},
|
||||
"bull_bear_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "跌包涨"},
|
||||
}
|
||||
|
||||
trades = []
|
||||
current_position = None # 当前持仓信息
|
||||
|
||||
for idx in range(1, len(all_data) - 1):
|
||||
prev, curr = all_data[idx - 1], all_data[idx]
|
||||
entry_candle = all_data[idx + 1]
|
||||
|
||||
direction, signal = check_signal(prev, curr)
|
||||
if not direction:
|
||||
continue
|
||||
|
||||
# 下一个 15 分钟K线的时间范围
|
||||
next_15min_time = all_data[idx + 50]['id'] if idx + 50 < len(all_data) else all_data[-1]['id']
|
||||
entry_price = float(entry_candle['open'])
|
||||
|
||||
# 有持仓
|
||||
if current_position:
|
||||
# 同向信号 -> 跳过
|
||||
if current_position['direction'] == direction:
|
||||
continue
|
||||
# 反向信号 -> 先平掉当前持仓,再开新仓
|
||||
else:
|
||||
# 先按当前位置止盈止损平仓
|
||||
exit_price, diff, exit_time = simulate_trade(
|
||||
current_position['direction'],
|
||||
current_position['entry_price'],
|
||||
current_position['entry_time'],
|
||||
entry_candle['id'],
|
||||
tp=tp,
|
||||
sl=sl
|
||||
)
|
||||
if exit_price is not None:
|
||||
trades.append({
|
||||
"entry_time": datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
"exit_time": datetime.datetime.fromtimestamp(exit_time / 1000),
|
||||
"signal": current_position['signal'],
|
||||
"direction": "做多" if current_position['direction'] == "long" else "做空",
|
||||
"entry": current_position['entry_price'],
|
||||
"exit": exit_price,
|
||||
"diff": diff
|
||||
})
|
||||
# 更新统计
|
||||
stats_key = 'bear_bull_engulf' if current_position['signal'] == '涨包跌' else 'bull_bear_engulf'
|
||||
stats[stats_key]['count'] += 1
|
||||
stats[stats_key]['total_profit'] += diff
|
||||
if diff > 0:
|
||||
stats[stats_key]['wins'] += 1
|
||||
|
||||
current_position = None # 清空持仓
|
||||
|
||||
# 开新仓
|
||||
current_position = {
|
||||
"direction": direction,
|
||||
"signal": stats[signal]['name'],
|
||||
"entry_price": entry_price,
|
||||
"entry_time": entry_candle['id']
|
||||
}
|
||||
|
||||
# 最后一笔持仓如果未平仓,用最后收盘价平掉
|
||||
if current_position:
|
||||
exit_price, diff, exit_time = simulate_trade(
|
||||
current_position['direction'],
|
||||
current_position['entry_price'],
|
||||
current_position['entry_time'],
|
||||
all_data[-1]['id'],
|
||||
tp=tp,
|
||||
sl=sl
|
||||
)
|
||||
if exit_price is not None:
|
||||
trades.append({
|
||||
"entry_time": datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
"exit_time": datetime.datetime.fromtimestamp(exit_time / 1000),
|
||||
"signal": current_position['signal'],
|
||||
"direction": "做多" if current_position['direction'] == "long" else "做空",
|
||||
"entry": current_position['entry_price'],
|
||||
"exit": exit_price,
|
||||
"diff": diff
|
||||
})
|
||||
stats_key = 'bear_bull_engulf' if current_position['signal'] == '涨包跌' else 'bull_bear_engulf'
|
||||
stats[stats_key]['count'] += 1
|
||||
stats[stats_key]['total_profit'] += diff
|
||||
if diff > 0:
|
||||
stats[stats_key]['wins'] += 1
|
||||
|
||||
return trades, stats
|
||||
|
||||
|
||||
# ===============================================================
|
||||
# 🚀 启动主流程
|
||||
# ===============================================================
|
||||
|
||||
if __name__ == '__main__':
|
||||
dates = [f"2025-10-{i}" for i in range(1, 31)]
|
||||
|
||||
trades, stats = backtest_single_position(dates, tp=50, sl=-10)
|
||||
|
||||
logger.info("===== 每笔交易详情 =====")
|
||||
for t in trades:
|
||||
logger.info(
|
||||
f"{t['entry_time']} {t['direction']}({t['signal']}) "
|
||||
f"入场={t['entry']:.2f} 出场={t['exit']:.2f} 出场时间={t['exit_time']} "
|
||||
f"差价={t['diff']:.2f}"
|
||||
)
|
||||
|
||||
total_profit = sum(t['diff'] / t['entry'] * 10000 for t in trades)
|
||||
total_fee = sum(5 + 10000 / t['entry'] * t['exit'] * 0.0005 for t in trades)
|
||||
|
||||
print(f"\n一共交易笔数:{len(trades)}")
|
||||
print(f"一共盈利:{total_profit:.2f}")
|
||||
print(f"一共手续费:{total_fee:.2f}")
|
||||
print(f"净利润:{total_profit - total_fee:.2f}")
|
||||
print("\n===== 信号统计 =====")
|
||||
|
||||
# ===============================================================================================================================
|
||||
|
||||
# for i in range(1, 16):
|
||||
# for i1 in range(1, 51):
|
||||
# trades, stats = backtest_single_position(dates, tp=i1, sl=-i)
|
||||
#
|
||||
# total_profit = sum(t['diff'] / t['entry'] * 10000 for t in trades)
|
||||
# total_fee = sum(5 + 10000 / t['entry'] * t['exit'] * 0.0005 for t in trades)
|
||||
#
|
||||
# if total_profit > total_fee * 0.1:
|
||||
# print("\n===== 信号统计 =====")
|
||||
# print(f"止盈:{i1}, 止损:{i}")
|
||||
# print(f"\n一共交易笔数:{len(trades)}")
|
||||
# print(f"一共盈利:{total_profit:.2f}")
|
||||
# print(f"一共手续费:{total_fee:.2f}")
|
||||
# print(f"净利润:{total_profit - total_fee * 0.1}")
|
||||
|
||||
# 需要优化,目前有两种情况,第一种,同向,不如说上一单开单是涨,上一单还没有结束,当前信号来了,就不开单,等上一单到了上一单的止损位或者止盈位在平仓
|
||||
# 第二种,方向,上一单是涨,上一单还没有结束,当前信号来了,是跌,然后就按照现在这个信号要开仓的位置,平掉上一单,然后开一单方向的,
|
||||
# 一笔中可能有好几次信号,都按照上面的规则去判断,要保证同一时间,只会有一笔持仓,
|
||||
# 打印每笔的交易详细,如果一笔中同向,输入为一条交易记录
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,312 +0,0 @@
|
||||
"""
|
||||
量化交易回测系统 - 优化版
|
||||
功能:基于包住形态的交易信号识别和回测分析
|
||||
作者:量化交易团队
|
||||
版本:2.0
|
||||
"""
|
||||
|
||||
import datetime
|
||||
from typing import List, Dict, Tuple, Optional, Any
|
||||
from dataclasses import dataclass
|
||||
from loguru import logger
|
||||
from peewee import fn
|
||||
from models.weex import Weex15, Weex1
|
||||
|
||||
|
||||
# ===============================================================
|
||||
# 📊 配置管理类
|
||||
# ===============================================================
|
||||
|
||||
@dataclass
|
||||
class BacktestConfig:
|
||||
"""回测配置类"""
|
||||
# 交易参数
|
||||
take_profit: float = 8.0 # 止盈点数
|
||||
stop_loss: float = -1.0 # 止损点数
|
||||
contract_size: float = 10000 # 合约规模
|
||||
open_fee: float = 5.0 # 开仓手续费
|
||||
close_fee_rate: float = 0.0005 # 平仓手续费率
|
||||
|
||||
# 回测日期范围
|
||||
start_date: str = "2025-7-1"
|
||||
end_date: str = "2025-7-31"
|
||||
|
||||
# 信号参数
|
||||
enable_bear_bull_engulf: bool = True # 涨包跌信号
|
||||
enable_bull_bear_engulf: bool = True # 跌包涨信号
|
||||
|
||||
def __post_init__(self):
|
||||
"""验证配置参数"""
|
||||
if self.take_profit <= 0:
|
||||
raise ValueError("止盈点数必须大于0")
|
||||
if self.stop_loss >= 0:
|
||||
raise ValueError("止损点数必须小于0")
|
||||
|
||||
|
||||
@dataclass
|
||||
class TradeRecord:
|
||||
"""交易记录类"""
|
||||
entry_time: datetime.datetime
|
||||
exit_time: datetime.datetime
|
||||
signal_type: str
|
||||
direction: str
|
||||
entry_price: float
|
||||
exit_price: float
|
||||
profit_loss: float
|
||||
profit_amount: float
|
||||
total_fee: float
|
||||
net_profit: float
|
||||
|
||||
|
||||
@dataclass
|
||||
class SignalStats:
|
||||
"""信号统计类"""
|
||||
signal_name: str
|
||||
count: int = 0
|
||||
wins: int = 0
|
||||
total_profit: float = 0.0
|
||||
|
||||
@property
|
||||
def win_rate(self) -> float:
|
||||
"""胜率计算"""
|
||||
return (self.wins / self.count * 100) if self.count > 0 else 0.0
|
||||
|
||||
@property
|
||||
def avg_profit(self) -> float:
|
||||
"""平均盈利"""
|
||||
return self.total_profit / self.count if self.count > 0 else 0.0
|
||||
|
||||
|
||||
# ===============================================================
|
||||
# 📊 数据获取模块
|
||||
# ===============================================================
|
||||
|
||||
def get_data_by_date(model, date_str):
|
||||
"""按天获取指定表的数据"""
|
||||
try:
|
||||
target_date = datetime.datetime.strptime(date_str, '%Y-%m-%d')
|
||||
except ValueError:
|
||||
logger.error("日期格式不正确,请使用 YYYY-MM-DD 格式。")
|
||||
return []
|
||||
|
||||
start_ts = int(target_date.timestamp() * 1000)
|
||||
end_ts = int((target_date + datetime.timedelta(days=1)).timestamp() * 1000) - 1
|
||||
|
||||
query = (model
|
||||
.select()
|
||||
.where(model.id.between(start_ts, end_ts))
|
||||
.order_by(model.id.asc()))
|
||||
|
||||
return [
|
||||
{'id': i.id, 'open': i.open, 'high': i.high, 'low': i.low, 'close': i.close}
|
||||
for i in query
|
||||
]
|
||||
|
||||
|
||||
def get_future_data_1min(start_ts, end_ts):
|
||||
"""获取指定时间范围内的 1 分钟数据"""
|
||||
query = (Weex1
|
||||
.select()
|
||||
.where(Weex1.id.between(start_ts, end_ts))
|
||||
.order_by(Weex1.id.asc()))
|
||||
return [{'id': i.id, 'open': i.open, 'high': i.high, 'low': i.low, 'close': i.close} for i in query]
|
||||
|
||||
|
||||
# ===============================================================
|
||||
# 📈 信号判定模块
|
||||
# ===============================================================
|
||||
|
||||
def is_bullish(c): return float(c['open']) < float(c['close'])
|
||||
|
||||
|
||||
def is_bearish(c): return float(c['open']) > float(c['close'])
|
||||
|
||||
|
||||
def check_signal(prev, curr):
|
||||
"""判断是否出现包住形态"""
|
||||
p_open, p_close = float(prev['open']), float(prev['close'])
|
||||
c_open, c_close = float(curr['open']), float(curr['close'])
|
||||
|
||||
# 前跌后涨包住 -> 做多
|
||||
if is_bullish(curr) and is_bearish(prev) and c_open <= p_close and c_close >= p_open:
|
||||
return "long", "bear_bull_engulf"
|
||||
|
||||
# 前涨后跌包住 -> 做空
|
||||
if is_bearish(curr) and is_bullish(prev) and c_open >= p_close and c_close <= p_open:
|
||||
return "short", "bull_bear_engulf"
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
# ===============================================================
|
||||
# 💹 回测模拟模块(使用 1 分钟数据)
|
||||
# ===============================================================
|
||||
|
||||
def simulate_trade(direction, entry_price, entry_time, next_15min_time, tp=8, sl=-1):
|
||||
"""
|
||||
用 1 分钟数据进行精细化止盈止损模拟
|
||||
entry_time: 当前信号的 entry candle id(毫秒时间戳)
|
||||
next_15min_time: 下一个15min时间戳,用于界定止盈止损分析范围
|
||||
|
||||
direction:信号类型
|
||||
entry_price:开仓价格
|
||||
entry_time:开仓时间
|
||||
next_15min_time:15分钟未来行情
|
||||
|
||||
"""
|
||||
# 查 15 分钟之间的 1 分钟数据
|
||||
future_candles = get_future_data_1min(entry_time, next_15min_time)
|
||||
if not future_candles:
|
||||
return None, 0, None
|
||||
|
||||
tp_price = entry_price + tp if direction == "long" else entry_price - tp # 止盈价位
|
||||
sl_price = entry_price + sl if direction == "long" else entry_price - sl # 止损价位
|
||||
|
||||
for candle in future_candles:
|
||||
open_p, high, low = map(float, (candle['open'], candle['high'], candle['low']))
|
||||
|
||||
if direction == "long": # long
|
||||
if open_p >= tp_price: # 开盘跳空止盈 涨信号,
|
||||
return open_p, open_p - entry_price, candle['id']
|
||||
if open_p <= sl_price: # 开盘跳空止损
|
||||
return open_p, open_p - entry_price, candle['id']
|
||||
if high >= tp_price:
|
||||
return tp_price, tp, candle['id']
|
||||
if low <= sl_price:
|
||||
return sl_price, sl, candle['id']
|
||||
|
||||
else: # short 跌信号
|
||||
if open_p <= tp_price: #
|
||||
return open_p, entry_price - open_p, candle['id']
|
||||
if open_p >= sl_price:
|
||||
return open_p, entry_price - open_p, candle['id']
|
||||
if low <= tp_price:
|
||||
return tp_price, tp, candle['id']
|
||||
if high >= sl_price:
|
||||
return sl_price, sl, candle['id']
|
||||
|
||||
# 未触发止盈止损,用最后一根收盘价平仓
|
||||
final = future_candles[-1]
|
||||
final_price = float(final['close'])
|
||||
diff = (final_price - entry_price) if direction == "long" else (entry_price - final_price)
|
||||
return final_price, diff, final['id']
|
||||
|
||||
|
||||
# ===============================================================
|
||||
# 📊 主回测流程
|
||||
# ===============================================================
|
||||
|
||||
def backtest(dates, tp, sl):
|
||||
"""
|
||||
datas:日期的列表
|
||||
|
||||
:param dates:
|
||||
:param tp:
|
||||
:param sl:
|
||||
:return:
|
||||
"""
|
||||
|
||||
all_data = []
|
||||
for date_str in dates:
|
||||
all_data.extend(get_data_by_date(Weex15, date_str)) # 获取每天的数据,15分钟k线数据
|
||||
|
||||
all_data.sort(key=lambda x: x['id'])
|
||||
|
||||
stats = {
|
||||
"bear_bull_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "涨包跌"},
|
||||
"bull_bear_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "跌包涨"},
|
||||
}
|
||||
|
||||
trades = []
|
||||
|
||||
for idx in range(1, len(all_data) - 1):
|
||||
prev, curr = all_data[idx - 1], all_data[idx] # 前一笔,当前一笔
|
||||
entry_candle = all_data[idx + 1] # 下一笔开仓k线
|
||||
|
||||
direction, signal = check_signal(prev, curr)
|
||||
if not direction:
|
||||
continue
|
||||
|
||||
# 下一个 15 分钟K线的时间范围
|
||||
next_15min_time = all_data[idx + 50]['id'] if idx + 50 < len(all_data) else all_data[-1]['id']
|
||||
|
||||
entry_price = float(entry_candle['open']) # 开仓价格
|
||||
exit_price, diff, exit_time = simulate_trade(
|
||||
direction,
|
||||
entry_price,
|
||||
entry_candle['id'],
|
||||
next_15min_time,
|
||||
tp=tp,
|
||||
sl=sl
|
||||
)
|
||||
|
||||
if exit_price is None:
|
||||
continue
|
||||
|
||||
stats[signal]['count'] += 1
|
||||
stats[signal]['total_profit'] += diff
|
||||
if diff > 0:
|
||||
stats[signal]['wins'] += 1
|
||||
|
||||
trades.append({
|
||||
"entry_time": datetime.datetime.fromtimestamp(entry_candle['id'] / 1000),
|
||||
"exit_time": datetime.datetime.fromtimestamp(exit_time / 1000),
|
||||
"signal": stats[signal]['name'],
|
||||
"direction": "做多" if direction == "long" else "做空",
|
||||
"entry": entry_price,
|
||||
"exit": exit_price,
|
||||
"diff": diff
|
||||
})
|
||||
|
||||
return trades, stats
|
||||
|
||||
|
||||
# ===============================================================
|
||||
# 🚀 启动主流程
|
||||
# ===============================================================
|
||||
|
||||
if __name__ == '__main__':
|
||||
dates = [f"2025-9-{i}" for i in range(1, 31)]
|
||||
|
||||
# for i in range(5, 11):
|
||||
# for i1 in range(25, 51):
|
||||
|
||||
trades, stats = backtest(dates, tp=50, sl=-10)
|
||||
|
||||
logger.info("===== 每笔交易详情 =====")
|
||||
for t in trades:
|
||||
logger.info(
|
||||
f"{t['entry_time']} {t['direction']}({t['signal']}) "
|
||||
f"入场={t['entry']:.2f} 出场={t['exit']:.2f} 出场时间={t['exit_time']} "
|
||||
f"差价={t['diff']:.2f}"
|
||||
)
|
||||
|
||||
total_profit = sum(t['diff'] / t['entry'] * 10000 for t in trades)
|
||||
total_fee = sum(5 + 10000 / t['entry'] * t['exit'] * 0.0005 for t in trades)
|
||||
# print(f"止盈:{i1}, 止损:{i}")
|
||||
|
||||
print(f"\n一共交易笔数:{len(trades)}")
|
||||
print(f"一共盈利:{total_profit:.2f}")
|
||||
print(f"一共手续费:{total_fee:.2f}")
|
||||
print(f"净利润:{total_profit - total_fee:.2f}")
|
||||
print("\n===== 信号统计 =====")
|
||||
|
||||
# if total_profit > total_fee * 0.1:
|
||||
# print(f"止盈:{i1}, 止损:{i}")
|
||||
# print(f"\n一共交易笔数:{len(trades)}")
|
||||
# print(f"一共盈利:{total_profit:.2f}")
|
||||
# print(f"一共手续费:{total_fee:.2f}")
|
||||
# print(f"净利润:{total_profit - total_fee * 0.1}")
|
||||
#
|
||||
# print("\n===== 信号统计 =====")
|
||||
#
|
||||
# for k, v in stats.items():
|
||||
# win_rate = (v['wins'] / v['count'] * 100) if v['count'] > 0 else 0
|
||||
# print(
|
||||
# f"{v['name']} ({k}) - 信号数: {v['count']} | 胜率: {win_rate:.2f}% | 总盈利: {v['total_profit']:.2f}")
|
||||
|
||||
|
||||
# 需要优化,目前有两种情况,第一种,同向,不如说上一单开单是涨,上一单还没有结束,当前信号来了,就不开单,等上一单到了上一单的止损位或者止盈位在平仓
|
||||
# 第二种,方向,上一单是涨,上一单还没有结束,当前信号来了,是跌,然后就按照现在这个信号要开仓的位置,平掉上一单,然后开一单方向的,
|
||||
# 一笔中可能有好几次信号,都按照上面的规则去判断,要保证同一时间,只会有一笔持仓,
|
||||
# 打印每笔的交易详细,如果一笔中同向,输入为一条交易记录,一条加以记录能够直观的看出中间有多少笔
|
||||
@@ -1,259 +0,0 @@
|
||||
"""
|
||||
量化交易回测系统 - 仅15分钟K线 & 信号续持/反手/单根反色平仓逻辑(完整版)
|
||||
"""
|
||||
|
||||
import datetime
|
||||
from dataclasses import dataclass
|
||||
from typing import List, Dict, Optional
|
||||
from loguru import logger
|
||||
from models.weex import Weex1Hour # 替换为你的15分钟K线模型
|
||||
|
||||
|
||||
# ========================= 工具函数 =========================
|
||||
|
||||
def is_bullish(c): # 阳线
|
||||
return float(c['close']) > float(c['open'])
|
||||
|
||||
|
||||
def is_bearish(c): # 阴线
|
||||
return float(c['close']) < float(c['open'])
|
||||
|
||||
|
||||
def check_signal(prev, curr):
|
||||
"""
|
||||
包住形态信号判定(仅15分钟K线):
|
||||
- 前跌后涨包住 -> 做多
|
||||
- 前涨后跌包住 -> 做空
|
||||
"""
|
||||
p_open, p_close = float(prev['open']), float(prev['close'])
|
||||
c_open, c_close = float(curr['open']), float(curr['close'])
|
||||
|
||||
# 前跌后涨包住 -> 做多
|
||||
if is_bullish(curr) and is_bearish(prev) and c_open <= p_close and c_close >= p_open:
|
||||
return "long", "bear_bull_engulf"
|
||||
|
||||
# 前涨后跌包住 -> 做空
|
||||
if is_bearish(curr) and is_bullish(prev) and c_open >= p_close and c_close <= p_open:
|
||||
return "short", "bull_bear_engulf"
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
def get_data_by_date(model, date_str: str):
|
||||
"""按天获取指定表的数据(15分钟)"""
|
||||
try:
|
||||
target_date = datetime.datetime.strptime(date_str, '%Y-%m-%d')
|
||||
except ValueError:
|
||||
logger.error("日期格式不正确,请使用 YYYY-MM-DD 格式。")
|
||||
return []
|
||||
|
||||
start_ts = int(target_date.timestamp() * 1000)
|
||||
end_ts = int((target_date + datetime.timedelta(days=1)).timestamp() * 1000) - 1
|
||||
|
||||
query = model.select().where(model.id.between(start_ts, end_ts)).order_by(model.id.asc())
|
||||
return [{'id': i.id, 'open': i.open, 'high': i.high, 'low': i.low, 'close': i.close} for i in query]
|
||||
|
||||
|
||||
# ========================= 回测逻辑 =========================
|
||||
|
||||
def backtest_15m_trend_optimized(dates: List[str]):
|
||||
all_data: List[Dict] = []
|
||||
for d in dates:
|
||||
all_data.extend(get_data_by_date(Weex1Hour, d))
|
||||
if not all_data:
|
||||
return [], {
|
||||
'bear_bull_engulf': {'count': 0, 'wins': 0, 'total_profit': 0.0, 'name': '涨包跌'},
|
||||
'bull_bear_engulf': {'count': 0, 'wins': 0, 'total_profit': 0.0, 'name': '跌包涨'},
|
||||
}
|
||||
|
||||
all_data.sort(key=lambda x: x['id'])
|
||||
|
||||
stats = {
|
||||
'bear_bull_engulf': {'count': 0, 'wins': 0, 'total_profit': 0.0, 'name': '涨包跌'},
|
||||
'bull_bear_engulf': {'count': 0, 'wins': 0, 'total_profit': 0.0, 'name': '跌包涨'},
|
||||
}
|
||||
|
||||
trades: List[Dict] = []
|
||||
current_position: Optional[Dict] = None
|
||||
idx = 1
|
||||
|
||||
while idx < len(all_data) - 1:
|
||||
prev, curr, next_bar = all_data[idx - 1], all_data[idx], all_data[idx + 1]
|
||||
direction, signal_key = check_signal(prev, curr)
|
||||
|
||||
# 空仓 -> 碰到信号则开仓
|
||||
if current_position is None and direction:
|
||||
entry_price = float(next_bar['open'])
|
||||
current_position = {
|
||||
'direction': direction,
|
||||
'signal': stats[signal_key]['name'],
|
||||
'signal_key': signal_key,
|
||||
'entry_price': entry_price,
|
||||
'entry_time': next_bar['id']
|
||||
}
|
||||
stats[signal_key]['count'] += 1
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
if current_position:
|
||||
pos_dir = current_position['direction']
|
||||
pos_sig_key = current_position['signal_key']
|
||||
|
||||
# 反向信号 -> 下一根开盘平仓 + 同价反手
|
||||
if direction and direction != pos_dir:
|
||||
exit_price = float(next_bar['open'])
|
||||
diff = (exit_price - current_position['entry_price']) if pos_dir == 'long' else (
|
||||
current_position['entry_price'] - exit_price)
|
||||
trades.append({
|
||||
'entry_time': datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
'exit_time': datetime.datetime.fromtimestamp(next_bar['id'] / 1000),
|
||||
'signal': current_position['signal'],
|
||||
'direction': '做多' if pos_dir == 'long' else '做空',
|
||||
'entry': current_position['entry_price'],
|
||||
'exit': exit_price,
|
||||
'diff': diff
|
||||
})
|
||||
stats[pos_sig_key]['total_profit'] += diff
|
||||
if diff > 0: stats[pos_sig_key]['wins'] += 1
|
||||
|
||||
current_position = {
|
||||
'direction': direction,
|
||||
'signal': stats[signal_key]['name'],
|
||||
'signal_key': signal_key,
|
||||
'entry_price': exit_price,
|
||||
'entry_time': next_bar['id']
|
||||
}
|
||||
stats[signal_key]['count'] += 1
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
# 同向信号 -> 续持
|
||||
if direction and direction == pos_dir:
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
# 单根反色K线 -> 判断后续是否能组成信号
|
||||
curr_is_opposite = (pos_dir == 'long' and is_bearish(curr)) or (pos_dir == 'short' and is_bullish(curr))
|
||||
if curr_is_opposite:
|
||||
can_peek = idx + 1 < len(all_data)
|
||||
if can_peek:
|
||||
lookahead_dir, _ = check_signal(curr, all_data[idx + 1])
|
||||
if lookahead_dir is not None:
|
||||
idx += 1
|
||||
continue # 后续可组成信号,等待信号处理
|
||||
|
||||
# 否则按收盘价平仓
|
||||
exit_price = float(next_bar['close'])
|
||||
diff = (exit_price - current_position['entry_price']) if pos_dir == 'long' else (
|
||||
current_position['entry_price'] - exit_price)
|
||||
trades.append({
|
||||
'entry_time': datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
'exit_time': datetime.datetime.fromtimestamp(curr['id'] / 1000),
|
||||
'signal': current_position['signal'],
|
||||
'direction': '做多' if pos_dir == 'long' else '做空',
|
||||
'entry': current_position['entry_price'],
|
||||
'exit': exit_price,
|
||||
'diff': diff
|
||||
})
|
||||
stats[pos_sig_key]['total_profit'] += diff
|
||||
if diff > 0: stats[pos_sig_key]['wins'] += 1
|
||||
current_position = None
|
||||
|
||||
idx += 1
|
||||
|
||||
# 尾仓:最后一根收盘价平仓
|
||||
if current_position:
|
||||
last = all_data[-1]
|
||||
exit_price = float(last['close'])
|
||||
pos_dir = current_position['direction']
|
||||
diff = (exit_price - current_position['entry_price']) if pos_dir == 'long' else (
|
||||
current_position['entry_price'] - exit_price)
|
||||
trades.append({
|
||||
'entry_time': datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
'exit_time': datetime.datetime.fromtimestamp(last['id'] / 1000),
|
||||
'signal': current_position['signal'],
|
||||
'direction': '做多' if pos_dir == 'long' else '做空',
|
||||
'entry': current_position['entry_price'],
|
||||
'exit': exit_price,
|
||||
'diff': diff
|
||||
})
|
||||
stats[current_position['signal_key']]['total_profit'] += diff
|
||||
if diff > 0: stats[current_position['signal_key']]['wins'] += 1
|
||||
|
||||
return trades, stats
|
||||
|
||||
|
||||
# ========================= 运行示例(优化版盈利计算) =========================
|
||||
if __name__ == '__main__':
|
||||
dates = []
|
||||
for i in range(1, 11):
|
||||
for i1 in range(1, 31):
|
||||
dates.append(f"2025-{f'0{i}' if len(str(i)) < 2 else i}-{i1}")
|
||||
#
|
||||
# print(dates)
|
||||
|
||||
# dates = [f"2025-10-{i}" for i in range(1, 31)]
|
||||
trades, stats = backtest_15m_trend_optimized(dates)
|
||||
|
||||
logger.info("===== 每笔交易详情 =====")
|
||||
|
||||
# === 参数设定 ===
|
||||
contract_size = 10000 # 合约规模(1手对应多少基础货币)
|
||||
open_fee_fixed = 5 # 固定开仓手续费
|
||||
close_fee_rate = 0.0005 # 按成交额比例的平仓手续费率
|
||||
|
||||
total_points_profit = 0 # 累计点差
|
||||
total_money_profit = 0 # 累计金额盈利
|
||||
total_fee = 0 # 累计手续费
|
||||
|
||||
for t in trades:
|
||||
entry = t['entry']
|
||||
exit = t['exit']
|
||||
direction = t['direction']
|
||||
|
||||
# === 1️⃣ 原始价差(点差) ===
|
||||
point_diff = (exit - entry) if direction == '做多' else (entry - exit)
|
||||
|
||||
# === 2️⃣ 金额盈利(考虑合约规模) ===
|
||||
money_profit = point_diff / entry * contract_size # 利润以基础货币计(例如USD)
|
||||
|
||||
# === 3️⃣ 手续费计算 ===
|
||||
# 开仓 + 平仓手续费(按比例计算 + 固定)
|
||||
fee = open_fee_fixed + (contract_size / entry * exit * close_fee_rate)
|
||||
|
||||
# === 4️⃣ 净利润 ===
|
||||
net_profit = money_profit - fee
|
||||
|
||||
# 保存计算结果
|
||||
t.update({
|
||||
'point_diff': point_diff,
|
||||
'raw_profit': money_profit,
|
||||
'fee': fee,
|
||||
'net_profit': net_profit
|
||||
})
|
||||
|
||||
total_points_profit += point_diff
|
||||
total_money_profit += money_profit
|
||||
total_fee += fee
|
||||
|
||||
# if net_profit > 500 or net_profit < -500:
|
||||
logger.info(
|
||||
f"{t['entry_time']} {direction}({t['signal']}) "
|
||||
f"入={entry:.2f} 出={exit:.2f} 差价={point_diff:.2f} "
|
||||
f"原始盈利={money_profit:.2f} 手续费={fee:.2f} 净利润={net_profit:.2f} {t['exit_time']}"
|
||||
)
|
||||
|
||||
# === 汇总统计 ===
|
||||
total_net_profit = total_money_profit - total_fee
|
||||
print(f"\n一共交易笔数:{len(trades)}")
|
||||
print(f"总点差:{total_points_profit:.2f}")
|
||||
print(f"总原始盈利(未扣费):{total_money_profit:.2f}")
|
||||
print(f"总手续费:{total_fee:.2f}")
|
||||
print(f"总净利润:{total_net_profit:.2f}\n")
|
||||
|
||||
print("===== 信号统计 =====")
|
||||
for k, v in stats.items():
|
||||
name, count, wins, total_p = v['name'], v['count'], v['wins'], v['total_profit']
|
||||
win_rate = (wins / count * 100) if count > 0 else 0.0
|
||||
avg_p = (total_p / count) if count > 0 else 0.0
|
||||
print(f"{name}: 次数={count} 胜率={win_rate:.2f}% 总价差={total_p:.2f} 平均价差={avg_p:.2f}")
|
||||
@@ -1,265 +0,0 @@
|
||||
"""
|
||||
量化交易回测系统 - 仅15分钟K线 & 反向K线即时平仓逻辑(完整版)
|
||||
"""
|
||||
|
||||
import datetime
|
||||
from dataclasses import dataclass
|
||||
from typing import List, Dict, Optional
|
||||
from loguru import logger
|
||||
from models.weex import Weex1Hour # 替换为你的15分钟K线模型
|
||||
|
||||
|
||||
# ========================= 工具函数 =========================
|
||||
|
||||
def is_bullish(c): # 阳线
|
||||
return float(c['close']) > float(c['open'])
|
||||
|
||||
|
||||
def is_bearish(c): # 阴线
|
||||
return float(c['close']) < float(c['open'])
|
||||
|
||||
|
||||
def check_signal(prev, curr):
|
||||
"""
|
||||
包住形态信号判定:
|
||||
- 前跌后涨包住 -> 做多
|
||||
- 前涨后跌包住 -> 做空
|
||||
"""
|
||||
p_open, p_close = float(prev['open']), float(prev['close'])
|
||||
c_open, c_close = float(curr['open']), float(curr['close'])
|
||||
|
||||
if is_bullish(curr) and is_bearish(prev) and c_open <= p_close and c_close >= p_open:
|
||||
return "long", "bear_bull_engulf"
|
||||
if is_bearish(curr) and is_bullish(prev) and c_open >= p_close and c_close <= p_open:
|
||||
return "short", "bull_bear_engulf"
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
def get_data_by_date(model, date_str: str):
|
||||
"""按天获取指定表的数据(15分钟K线)"""
|
||||
try:
|
||||
target_date = datetime.datetime.strptime(date_str, '%Y-%m-%d')
|
||||
except ValueError:
|
||||
logger.error("日期格式不正确,请使用 YYYY-MM-DD 格式。")
|
||||
return []
|
||||
|
||||
start_ts = int(target_date.timestamp() * 1000)
|
||||
end_ts = int((target_date + datetime.timedelta(days=1)).timestamp() * 1000) - 1
|
||||
|
||||
query = model.select().where(model.id.between(start_ts, end_ts)).order_by(model.id.asc())
|
||||
return [{'id': i.id, 'open': i.open, 'high': i.high, 'low': i.low, 'close': i.close} for i in query]
|
||||
|
||||
|
||||
# ========================= 回测逻辑 =========================
|
||||
|
||||
def backtest_15m_trend_simplified(dates: List[str]):
|
||||
all_data: List[Dict] = []
|
||||
for d in dates:
|
||||
all_data.extend(get_data_by_date(Weex1Hour, d))
|
||||
if not all_data:
|
||||
return [], {
|
||||
'bear_bull_engulf': {'count': 0, 'wins': 0, 'total_profit': 0.0, 'name': '涨包跌'},
|
||||
'bull_bear_engulf': {'count': 0, 'wins': 0, 'total_profit': 0.0, 'name': '跌包涨'},
|
||||
}
|
||||
|
||||
all_data.sort(key=lambda x: x['id'])
|
||||
|
||||
stats = {
|
||||
'bear_bull_engulf': {'count': 0, 'wins': 0, 'total_profit': 0.0, 'name': '涨包跌'},
|
||||
'bull_bear_engulf': {'count': 0, 'wins': 0, 'total_profit': 0.0, 'name': '跌包涨'},
|
||||
}
|
||||
|
||||
trades: List[Dict] = []
|
||||
current_position: Optional[Dict] = None
|
||||
idx = 1
|
||||
|
||||
while idx < len(all_data) - 1:
|
||||
prev, curr, next_bar = all_data[idx - 1], all_data[idx], all_data[idx + 1]
|
||||
direction, signal_key = check_signal(prev, curr)
|
||||
|
||||
# 空仓 -> 碰到信号则开仓
|
||||
if current_position is None and direction:
|
||||
entry_price = float(next_bar['open'])
|
||||
current_position = {
|
||||
'direction': direction,
|
||||
'signal': stats[signal_key]['name'],
|
||||
'signal_key': signal_key,
|
||||
'entry_price': entry_price,
|
||||
'entry_time': next_bar['id']
|
||||
}
|
||||
stats[signal_key]['count'] += 1
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
if current_position:
|
||||
pos_dir = current_position['direction']
|
||||
pos_sig_key = current_position['signal_key']
|
||||
|
||||
# 反向信号 -> 平仓 + 反手
|
||||
if direction and direction != pos_dir:
|
||||
exit_price = float(next_bar['open'])
|
||||
diff = (exit_price - current_position['entry_price']) if pos_dir == 'long' else (
|
||||
current_position['entry_price'] - exit_price)
|
||||
trades.append({
|
||||
'entry_time': datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
'exit_time': datetime.datetime.fromtimestamp(next_bar['id'] / 1000),
|
||||
'signal': current_position['signal'],
|
||||
'direction': '做多' if pos_dir == 'long' else '做空',
|
||||
'entry': current_position['entry_price'],
|
||||
'exit': exit_price,
|
||||
'diff': diff
|
||||
})
|
||||
stats[pos_sig_key]['total_profit'] += diff
|
||||
if diff > 0:
|
||||
stats[pos_sig_key]['wins'] += 1
|
||||
|
||||
# 反手开仓
|
||||
current_position = {
|
||||
'direction': direction,
|
||||
'signal': stats[signal_key]['name'],
|
||||
'signal_key': signal_key,
|
||||
'entry_price': exit_price,
|
||||
'entry_time': next_bar['id']
|
||||
}
|
||||
stats[signal_key]['count'] += 1
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
# === 新逻辑:遇到反向K线立即平仓 ===
|
||||
if pos_dir == 'long' and is_bearish(curr):
|
||||
exit_price = float(curr['close'])
|
||||
diff = exit_price - current_position['entry_price']
|
||||
trades.append({
|
||||
'entry_time': datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
'exit_time': datetime.datetime.fromtimestamp(curr['id'] / 1000),
|
||||
'signal': current_position['signal'],
|
||||
'direction': '做多',
|
||||
'entry': current_position['entry_price'],
|
||||
'exit': exit_price,
|
||||
'diff': diff
|
||||
})
|
||||
stats[pos_sig_key]['total_profit'] += diff
|
||||
if diff > 0:
|
||||
stats[pos_sig_key]['wins'] += 1
|
||||
current_position = None
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
if pos_dir == 'short' and is_bullish(curr):
|
||||
exit_price = float(curr['close'])
|
||||
diff = current_position['entry_price'] - exit_price
|
||||
trades.append({
|
||||
'entry_time': datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
'exit_time': datetime.datetime.fromtimestamp(curr['id'] / 1000),
|
||||
'signal': current_position['signal'],
|
||||
'direction': '做空',
|
||||
'entry': current_position['entry_price'],
|
||||
'exit': exit_price,
|
||||
'diff': diff
|
||||
})
|
||||
stats[pos_sig_key]['total_profit'] += diff
|
||||
if diff > 0:
|
||||
stats[pos_sig_key]['wins'] += 1
|
||||
current_position = None
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
idx += 1
|
||||
|
||||
# 尾仓平仓
|
||||
if current_position:
|
||||
last = all_data[-1]
|
||||
exit_price = float(last['close'])
|
||||
pos_dir = current_position['direction']
|
||||
diff = (exit_price - current_position['entry_price']) if pos_dir == 'long' else (
|
||||
current_position['entry_price'] - exit_price)
|
||||
trades.append({
|
||||
'entry_time': datetime.datetime.fromtimestamp(current_position['entry_time'] / 1000),
|
||||
'exit_time': datetime.datetime.fromtimestamp(last['id'] / 1000),
|
||||
'signal': current_position['signal'],
|
||||
'direction': '做多' if pos_dir == 'long' else '做空',
|
||||
'entry': current_position['entry_price'],
|
||||
'exit': exit_price,
|
||||
'diff': diff
|
||||
})
|
||||
stats[current_position['signal_key']]['total_profit'] += diff
|
||||
if diff > 0:
|
||||
stats[current_position['signal_key']]['wins'] += 1
|
||||
|
||||
return trades, stats
|
||||
|
||||
|
||||
# ========================= 运行示例 =========================
|
||||
if __name__ == '__main__':
|
||||
# dates = [f"2025-06-{i}" for i in range(1, 31)]
|
||||
|
||||
dates = []
|
||||
for i in range(1, 11):
|
||||
for i1 in range(1, 31):
|
||||
dates.append(f"2025-{f'0{i}' if len(str(i)) < 2 else i}-{f'0{i1}' if len(str(i1)) < 2 else i1}")
|
||||
|
||||
|
||||
|
||||
trades, stats = backtest_15m_trend_simplified(dates)
|
||||
|
||||
logger.info("===== 每笔交易详情 =====")
|
||||
|
||||
# === 参数设定 ===
|
||||
contract_size = 10000 # 合约规模(1手对应多少基础货币)
|
||||
open_fee_fixed = 5 # 固定开仓手续费
|
||||
close_fee_rate = 0.0005 # 按成交额比例的平仓手续费率
|
||||
|
||||
total_points_profit = 0 # 累计点差
|
||||
total_money_profit = 0 # 累计金额盈利
|
||||
total_fee = 0 # 累计手续费
|
||||
|
||||
for t in trades:
|
||||
entry = t['entry']
|
||||
exit = t['exit']
|
||||
direction = t['direction']
|
||||
|
||||
# === 1️⃣ 原始价差(点差) ===
|
||||
point_diff = (exit - entry) if direction == '做多' else (entry - exit)
|
||||
|
||||
# === 2️⃣ 金额盈利(考虑合约规模) ===
|
||||
money_profit = point_diff / entry * contract_size # 利润以基础货币计(例如USD)
|
||||
|
||||
# === 3️⃣ 手续费计算 ===
|
||||
fee = open_fee_fixed + (contract_size / entry * exit * close_fee_rate)
|
||||
|
||||
# === 4️⃣ 净利润 ===
|
||||
net_profit = money_profit - fee
|
||||
|
||||
# 保存计算结果
|
||||
t.update({
|
||||
'point_diff': point_diff,
|
||||
'raw_profit': money_profit,
|
||||
'fee': fee,
|
||||
'net_profit': net_profit
|
||||
})
|
||||
|
||||
total_points_profit += point_diff
|
||||
total_money_profit += money_profit
|
||||
total_fee += fee
|
||||
|
||||
logger.info(
|
||||
f"{t['entry_time']} {direction}({t['signal']}) "
|
||||
f"入={entry:.2f} 出={exit:.2f} 差价={point_diff:.2f} "
|
||||
f"原始盈利={money_profit:.2f} 手续费={fee:.2f} 净利润={net_profit:.2f} {t['exit_time']}"
|
||||
)
|
||||
|
||||
# === 汇总统计 ===
|
||||
total_net_profit = total_money_profit - total_fee
|
||||
print(f"\n一共交易笔数:{len(trades)}")
|
||||
print(f"总点差:{total_points_profit:.2f}")
|
||||
print(f"总原始盈利(未扣费):{total_money_profit:.2f}")
|
||||
print(f"总手续费:{total_fee:.2f}")
|
||||
print(f"总净利润:{total_net_profit:.2f}\n")
|
||||
|
||||
print("===== 信号统计 =====")
|
||||
for k, v in stats.items():
|
||||
name, count, wins, total_p = v['name'], v['count'], v['wins'], v['total_profit']
|
||||
win_rate = (wins / count * 100) if count > 0 else 0.0
|
||||
avg_p = (total_p / count) if count > 0 else 0.0
|
||||
print(f"{name}: 次数={count} 胜率={win_rate:.2f}% 总价差={total_p:.2f} 平均价差={avg_p:.2f}")
|
||||
244
weex/读取文件分析.py
244
weex/读取文件分析.py
@@ -1,244 +0,0 @@
|
||||
import datetime
|
||||
|
||||
import pandas as pd
|
||||
from pathlib import Path
|
||||
|
||||
from loguru import logger
|
||||
|
||||
|
||||
# ================= 辅助函数 =================
|
||||
|
||||
def is_bullish(candle):
|
||||
"""判断是否是阳线(开盘价 < 收盘价,即涨)"""
|
||||
return float(candle['open']) < float(candle['close'])
|
||||
|
||||
|
||||
def is_bearish(candle):
|
||||
"""判断是否是阴线(开盘价 > 收盘价,即跌)"""
|
||||
return float(candle['open']) > float(candle['close'])
|
||||
|
||||
|
||||
def check_signal(prev, curr):
|
||||
"""
|
||||
判断是否出现包住形态,返回信号类型和方向:
|
||||
1. 前跌后涨包住 -> 做多信号 (bear_bull_engulf)
|
||||
2. 前涨后跌包住 -> 做空信号 (bull_bear_engulf)
|
||||
3. 前涨后涨包住 -> 做多信号 (bull_bull_engulf)
|
||||
4. 前跌后跌包住 -> 做空信号 (bear_bear_engulf)
|
||||
"""
|
||||
p_open, p_close = float(prev['open']), float(prev['close']) # 前一笔
|
||||
c_open, c_close = float(curr['open']), float(curr['close']) # 当前一笔
|
||||
|
||||
# 情况1:前跌后涨,且涨线包住前跌线 -> 做多信号
|
||||
if is_bullish(curr) and is_bearish(prev):
|
||||
if c_open <= p_close and c_close >= p_open:
|
||||
return "long", "bear_bull_engulf"
|
||||
|
||||
# 情况2:前涨后跌,且跌线包住前涨线 -> 做空信号
|
||||
if is_bearish(curr) and is_bullish(prev):
|
||||
if c_open >= p_close and c_close <= p_open:
|
||||
return "short", "bull_bear_engulf"
|
||||
|
||||
# # 情况3:前涨后涨,且后涨线包住前涨线 -> 做多信号
|
||||
# if is_bullish(curr) and is_bullish(prev):
|
||||
# if c_open < p_open and c_close > p_close:
|
||||
# return "long", "bull_bull_engulf"
|
||||
|
||||
# # 情况4:前跌后跌,且后跌线包住前跌线 -> 做空信号
|
||||
# if is_bearish(curr) and is_bearish(prev):
|
||||
# if c_open > p_open and c_close < p_close:
|
||||
# return "short", "bear_bear_engulf"
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
def simulate_trade(direction, entry_price, future_candles, take_profit_diff=30, stop_loss_diff=-10):
|
||||
"""
|
||||
模拟交易(逐根K线回测)
|
||||
改进版:考虑开盘跳空触发止盈/止损的情况
|
||||
"""
|
||||
for candle in future_candles:
|
||||
open_p = float(candle['open'])
|
||||
high = float(candle['high'])
|
||||
low = float(candle['low'])
|
||||
close = float(candle['close'])
|
||||
|
||||
if direction == "long":
|
||||
tp_price = entry_price + take_profit_diff
|
||||
sl_price = entry_price + stop_loss_diff
|
||||
|
||||
# 🧩 开盘就跳空止盈
|
||||
if open_p >= tp_price:
|
||||
return open_p, open_p - entry_price, candle['id']
|
||||
|
||||
# 🧩 开盘就跳空止损
|
||||
if open_p <= sl_price:
|
||||
return open_p, open_p - entry_price, candle['id']
|
||||
|
||||
# 正常区间内触发止盈/止损
|
||||
if high >= tp_price:
|
||||
return tp_price, take_profit_diff, candle['id']
|
||||
if low <= sl_price:
|
||||
return sl_price, stop_loss_diff, candle['id']
|
||||
|
||||
elif direction == "short":
|
||||
tp_price = entry_price - take_profit_diff
|
||||
sl_price = entry_price - stop_loss_diff
|
||||
|
||||
# 🧩 开盘就跳空止盈
|
||||
if open_p <= tp_price:
|
||||
return open_p, entry_price - open_p, candle['id']
|
||||
|
||||
# 🧩 开盘就跳空止损
|
||||
if open_p >= sl_price:
|
||||
return open_p, entry_price - open_p, candle['id']
|
||||
|
||||
# 正常区间内触发止盈/止损
|
||||
if low <= tp_price:
|
||||
return tp_price, take_profit_diff, candle['id']
|
||||
if high >= sl_price:
|
||||
return sl_price, stop_loss_diff, candle['id']
|
||||
|
||||
# 未触发止盈止损,按最后收盘价平仓
|
||||
final_price = float(future_candles[-1]['close'])
|
||||
if direction == "long":
|
||||
diff_money = final_price - entry_price
|
||||
else:
|
||||
diff_money = entry_price - final_price
|
||||
|
||||
return final_price, diff_money, future_candles[-1]['id']
|
||||
|
||||
|
||||
|
||||
def fetch_kline(day: int, year: int = 2025, month: int = 9, file_path: str = "stock_data.xlsx") -> list[dict]:
|
||||
"""
|
||||
获取指定日期的分钟级 K 线数据。
|
||||
|
||||
参数:
|
||||
day (int): 日
|
||||
year (int): 年(默认 2025)
|
||||
month (int): 月(默认 9)
|
||||
file_path (str): Excel 文件路径
|
||||
|
||||
返回:
|
||||
list[dict]: 当日的 K 线记录(按时间升序且去重)
|
||||
"""
|
||||
try:
|
||||
# 验证文件路径
|
||||
file = Path(file_path)
|
||||
if not file.exists():
|
||||
raise FileNotFoundError(f"未找到文件: {file.resolve()}")
|
||||
|
||||
# 计算起止时间戳
|
||||
start_of_day = datetime.datetime(year, month, day)
|
||||
end_of_day = start_of_day + pd.Timedelta(days=1) - pd.Timedelta(microseconds=1)
|
||||
start_ts, end_ts = int(start_of_day.timestamp() * 1000), int(end_of_day.timestamp() * 1000)
|
||||
|
||||
# 读取 Excel
|
||||
df = pd.read_excel(file)
|
||||
|
||||
# 校验列名
|
||||
if 'id' not in df.columns:
|
||||
raise KeyError("Excel 文件缺少必要列:'id'")
|
||||
|
||||
# 筛选并排序 + 去重
|
||||
filtered_df = (
|
||||
df.loc[df['id'].between(start_ts, end_ts)]
|
||||
.sort_values('id')
|
||||
.drop_duplicates(subset='id', keep='first')
|
||||
)
|
||||
|
||||
# 转换为字典列表
|
||||
return filtered_df.to_dict(orient='records')
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ 错误: {e}")
|
||||
return []
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
datas = []
|
||||
for i in range(1, 31):
|
||||
data = fetch_kline(year=2025, month=9, day=i)
|
||||
datas.extend(data)
|
||||
|
||||
print(i)
|
||||
|
||||
print(len(datas))
|
||||
|
||||
datas = sorted(datas, key=lambda x: x["id"])
|
||||
|
||||
zh_project = 0 # 累计盈亏
|
||||
all_trades = [] # 保存所有交易明细
|
||||
|
||||
# 四种信号类型的统计
|
||||
signal_stats = {
|
||||
"bear_bull_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "涨包跌"},
|
||||
"bull_bear_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "跌包涨"},
|
||||
# "bull_bull_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "涨包涨"},
|
||||
# "bear_bear_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "跌包跌"}
|
||||
}
|
||||
|
||||
daily_signals = 0 # 信号总数
|
||||
daily_wins = 0
|
||||
daily_profit = 0 # 价差总和
|
||||
|
||||
# 遍历每根K线,寻找信号
|
||||
for idx in range(1, len(datas) - 2): # 留出未来K线
|
||||
prev, curr = datas[idx - 1], datas[idx] # 前一笔,当前一笔
|
||||
entry_candle = datas[idx + 1] # 下一根开盘价作为入场价
|
||||
future_candles = datas[idx + 2:] # 未来行情
|
||||
|
||||
entry_open = float(entry_candle['open']) # 开仓价格
|
||||
direction, signal_type = check_signal(prev, curr) # 判断开仓方向和信号类型
|
||||
|
||||
if direction and signal_type:
|
||||
daily_signals += 1
|
||||
|
||||
exit_price, diff, exit_time = simulate_trade(direction, entry_open, future_candles, take_profit_diff=30,
|
||||
stop_loss_diff=-5)
|
||||
|
||||
# 统计该信号类型的表现
|
||||
signal_stats[signal_type]["count"] += 1
|
||||
signal_stats[signal_type]["total_profit"] += diff
|
||||
if diff > 0:
|
||||
signal_stats[signal_type]["wins"] += 1
|
||||
daily_wins += 1
|
||||
|
||||
daily_profit += diff
|
||||
|
||||
# 将时间戳转换为本地时间
|
||||
local_time = datetime.datetime.fromtimestamp(int(entry_candle['id']) / 1000)
|
||||
formatted_time = local_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
# 保存交易详情
|
||||
all_trades.append(
|
||||
(
|
||||
f"{formatted_time}号",
|
||||
"做多" if direction == "long" else "做空",
|
||||
signal_stats[signal_type]["name"],
|
||||
entry_open,
|
||||
exit_price,
|
||||
diff,
|
||||
exit_time
|
||||
)
|
||||
)
|
||||
|
||||
# ===== 输出每笔交易详情 =====
|
||||
logger.info("===== 每笔交易详情 =====")
|
||||
n = n1 = 0 # n = 总盈利,n1 = 总手续费
|
||||
for date, direction, signal_name, entry, exit, diff, end_time in all_trades:
|
||||
profit_amount = diff / entry * 10000 # 计算盈利金额
|
||||
close_fee = 10000 / entry * exit * 0.0005 # 平仓手续费
|
||||
|
||||
logger.info(
|
||||
f"{date} {direction}({signal_name}) 入场={entry:.2f} 出场={exit:.2f} 出场时间={end_time} "
|
||||
f"差价={diff:.2f} 盈利={profit_amount:.2f} "
|
||||
f"开仓手续费=5u 平仓手续费={close_fee:.2f}"
|
||||
)
|
||||
n1 += 5 + close_fee
|
||||
n += profit_amount
|
||||
|
||||
print(f'一共笔数:{len(all_trades)}')
|
||||
print(f"一共盈利:{n:.2f}")
|
||||
print(f'一共手续费:{n1:.2f}')
|
||||
@@ -1,90 +0,0 @@
|
||||
"""
|
||||
调试信号检测逻辑,查看为什么没有检测到交易信号
|
||||
"""
|
||||
import datetime
|
||||
from models.weex import Weex30
|
||||
|
||||
def get_data_by_date(model, date_str: str):
|
||||
"""按天获取指定表的数据"""
|
||||
try:
|
||||
target_date = datetime.datetime.strptime(date_str, '%Y-%m-%d')
|
||||
except ValueError:
|
||||
print(f"日期格式不正确: {date_str}")
|
||||
return []
|
||||
|
||||
start_ts = int(target_date.timestamp() * 1000)
|
||||
end_ts = int((target_date + datetime.timedelta(days=1)).timestamp() * 1000) - 1
|
||||
|
||||
query = model.select().where(model.id.between(start_ts, end_ts)).order_by(model.id.asc())
|
||||
return [{'id': i.id, 'open': i.open, 'high': i.high, 'low': i.low, 'close': i.close} for i in query]
|
||||
|
||||
def is_bullish(c):
|
||||
return float(c['close']) > float(c['open'])
|
||||
|
||||
def is_bearish(c):
|
||||
return float(c['close']) < float(c['open'])
|
||||
|
||||
def check_signal(prev, curr):
|
||||
"""包住形态信号判定"""
|
||||
p_open, p_close = float(prev['open']), float(prev['close'])
|
||||
c_open, c_close = float(curr['open']), float(curr['close'])
|
||||
|
||||
# 前跌后涨包住 -> 做多
|
||||
if is_bullish(curr) and is_bearish(prev) and c_open <= p_close and c_close >= p_open:
|
||||
return "long", "bear_bull_engulf"
|
||||
|
||||
# 前涨后跌包住 -> 做空
|
||||
if is_bearish(curr) and is_bullish(prev) and c_open >= p_close and c_close <= p_open:
|
||||
return "short", "bull_bear_engulf"
|
||||
|
||||
return None, None
|
||||
|
||||
# 获取数据
|
||||
dates = []
|
||||
for i in range(2, 32):
|
||||
dates.append(f"2025-12-{i:02d}")
|
||||
for i in range(1, 5):
|
||||
dates.append(f"2026-01-{i:02d}")
|
||||
|
||||
all_data = []
|
||||
for d in dates:
|
||||
day_data = get_data_by_date(Weex30, d)
|
||||
all_data.extend(day_data)
|
||||
if day_data:
|
||||
print(f"{d}: 读取到 {len(day_data)} 条数据")
|
||||
|
||||
print(f"\n总共读取到 {len(all_data)} 条数据")
|
||||
|
||||
if len(all_data) < 2:
|
||||
print("数据不足,无法检测信号(至少需要2条K线)")
|
||||
exit()
|
||||
|
||||
# 排序
|
||||
all_data.sort(key=lambda x: x['id'])
|
||||
|
||||
# 检查信号
|
||||
signal_count = 0
|
||||
for idx in range(1, len(all_data)):
|
||||
prev = all_data[idx - 1]
|
||||
curr = all_data[idx]
|
||||
direction, signal_key = check_signal(prev, curr)
|
||||
|
||||
if direction:
|
||||
signal_count += 1
|
||||
prev_time = datetime.datetime.fromtimestamp(prev['id'] / 1000)
|
||||
curr_time = datetime.datetime.fromtimestamp(curr['id'] / 1000)
|
||||
print(f"\n信号 #{signal_count}: {signal_key} ({direction})")
|
||||
print(f" 前一根K线 ({prev_time}): O={prev['open']:.2f} H={prev['high']:.2f} L={prev['low']:.2f} C={prev['close']:.2f} {'阳' if is_bullish(prev) else '阴'}")
|
||||
print(f" 当前K线 ({curr_time}): O={curr['open']:.2f} H={curr['high']:.2f} L={curr['low']:.2f} C={curr['close']:.2f} {'阳' if is_bullish(curr) else '阴'}")
|
||||
print(f" 包住条件检查:")
|
||||
print(f" - 前跌后涨: {is_bearish(prev)} and {is_bullish(curr)}")
|
||||
print(f" - 开盘<=前收盘: {float(curr['open'])} <= {float(prev['close'])} = {float(curr['open']) <= float(prev['close'])}")
|
||||
print(f" - 收盘>=前开盘: {float(curr['close'])} >= {float(prev['open'])} = {float(curr['close']) >= float(prev['open'])}")
|
||||
|
||||
if signal_count == 0:
|
||||
print("\n未检测到任何信号!")
|
||||
print("\n检查前10根K线的情况:")
|
||||
for idx in range(min(10, len(all_data))):
|
||||
k = all_data[idx]
|
||||
k_time = datetime.datetime.fromtimestamp(k['id'] / 1000)
|
||||
print(f" {idx}: {k_time} O={k['open']:.2f} C={k['close']:.2f} {'阳' if is_bullish(k) else '阴'}")
|
||||
@@ -1,410 +0,0 @@
|
||||
import re
|
||||
import json
|
||||
import hmac
|
||||
import time
|
||||
import base64
|
||||
import hashlib
|
||||
import datetime
|
||||
import requests
|
||||
|
||||
from tqdm import *
|
||||
from loguru import *
|
||||
from DrissionPage import *
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
|
||||
def is_bullish(c): # 阳线
|
||||
return float(c['close']) > float(c['open'])
|
||||
|
||||
|
||||
def is_bearish(c): # 阴线
|
||||
return float(c['close']) < float(c['open'])
|
||||
|
||||
|
||||
class WeexTransaction:
|
||||
def __init__(self, tge_id):
|
||||
self.tge_port = None # tge浏览器使用端口
|
||||
self.tge_id = tge_id # tge id
|
||||
self.tge_url = "http://127.0.0.1:50326" # tge本地服务url
|
||||
self.tge_headers = {
|
||||
"Authorization": f"Bearer asp_174003986c9b0799677c5b2c1adb76e402735d753bc91a91",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
# 替换为你自己的钉钉机器人 Webhook 地址
|
||||
self.webhook_url = "https://oapi.dingtalk.com/robot/send?access_token=e2fafb3f46866d50fe52cbb29650ba9ef1cbc97915dde238192f04c906fe4125"
|
||||
# 替换为你自己的钉钉机器人秘钥
|
||||
self.secret = "SEC5f320e72d7a4eaca540c66c3d09edff2f74936517390dee99ece6dd1b3611998"
|
||||
|
||||
self.page = None # 浏览器对象
|
||||
|
||||
self.start = 0 # 持仓状态 -1:做空,0:维持仓,1:做多
|
||||
self.kline_1 = None # 0:跌,1:涨
|
||||
self.kline_2 = None # 0:跌,1:涨
|
||||
|
||||
self.direction = None # 信号类型
|
||||
|
||||
self.pbar = None # 进度条对象
|
||||
|
||||
def get_signature(self, timestamp):
|
||||
# 将时间戳和密钥拼接
|
||||
string_to_sign = f'{timestamp}\n{self.secret}'
|
||||
string_to_sign = string_to_sign.encode('utf-8')
|
||||
# 使用 HMAC-SHA256 算法进行签名
|
||||
hmac_code = hmac.new(self.secret.encode('utf-8'), string_to_sign, digestmod=hashlib.sha256).digest()
|
||||
# 对签名结果进行 Base64 编码
|
||||
sign = base64.b64encode(hmac_code).decode('utf-8')
|
||||
return sign
|
||||
|
||||
# def send_dingtalk_message(self, message_content):
|
||||
# # 获取当前时间戳(毫秒)
|
||||
# timestamp = str(round(time.time() * 1000))
|
||||
# # 生成签名
|
||||
# sign = self.get_signature(timestamp, )
|
||||
# # 拼接带有签名信息的完整 Webhook URL
|
||||
# full_url = f"{self.webhook_url}×tamp={timestamp}&sign={sign}"
|
||||
#
|
||||
# # 定义消息内容
|
||||
# message = {
|
||||
# "msgtype": "text",
|
||||
# "text": {
|
||||
# "content": message_content
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# # 设置请求头
|
||||
# headers = {
|
||||
# "Content-Type": "application/json"
|
||||
# }
|
||||
#
|
||||
# try:
|
||||
# # 发送 POST 请求
|
||||
# response = requests.post(full_url, headers=headers, data=json.dumps(message))
|
||||
#
|
||||
# except requests.RequestException as e:
|
||||
# print(f"请求发生错误: {e}")
|
||||
|
||||
def send_dingtalk_message(self, message_content):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
# url = "http://8.137.99.82:9005/api/send_click?token=fegergauiernguie&phone=8613661496481"
|
||||
#
|
||||
# res = requests.post(
|
||||
# url=url,
|
||||
# json={
|
||||
# "phone": "8613661496481",
|
||||
# "bot_name": "ergggreef",
|
||||
# "datas": [
|
||||
# {"send_message": [message_content], "click_button": [""], },
|
||||
# ]
|
||||
#
|
||||
# }
|
||||
# )
|
||||
#
|
||||
# print(res.json())
|
||||
|
||||
def openBrowser(self, ): # 直接指定ID打开窗口,也可以使用 createBrowser 方法返回的ID
|
||||
|
||||
try:
|
||||
|
||||
response = requests.post(
|
||||
f"{self.tge_url}/api/browser/start",
|
||||
json={"envId": self.tge_id},
|
||||
headers=self.tge_headers
|
||||
)
|
||||
|
||||
self.tge_port = response.json()["data"]["port"]
|
||||
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def take_over_browser(self):
|
||||
try:
|
||||
co = ChromiumOptions()
|
||||
co.set_local_port(self.tge_port)
|
||||
|
||||
self.page = ChromiumPage(addr_or_opts=co)
|
||||
|
||||
self.page.set.window.max()
|
||||
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def is_bullish(self, c): # 阳线
|
||||
return float(c['close']) > float(c['open'])
|
||||
|
||||
def is_bearish(self, c): # 阴线
|
||||
return float(c['close']) < float(c['open'])
|
||||
|
||||
def check_signal(self, prev, curr):
|
||||
"""
|
||||
包住形态信号判定(仅15分钟K线):
|
||||
- 前跌后涨包住 -> 做多
|
||||
- 前涨后跌包住 -> 做空
|
||||
"""
|
||||
p_open, p_close = float(prev['open']), float(prev['close'])
|
||||
c_open, c_close = float(curr['open']), float(curr['close'])
|
||||
|
||||
# 前跌后涨包住 -> 做多
|
||||
if is_bullish(curr) and is_bearish(prev) and c_open <= p_close and c_close >= p_open:
|
||||
return "long", "bear_bull_engulf"
|
||||
|
||||
# 前涨后跌包住 -> 做空
|
||||
if is_bearish(curr) and is_bullish(prev) and c_open >= p_close and c_close <= p_open:
|
||||
return "short", "bull_bear_engulf"
|
||||
|
||||
return None, None
|
||||
|
||||
def get_price(self):
|
||||
|
||||
for i in range(3):
|
||||
try:
|
||||
logger.info(f"获取最新数据:{i + 1}次。。。")
|
||||
self.mn_tab.get(url="https://www.weeaxs.site/zh-CN/futures/demo-trading/ETH-SUSDT")
|
||||
res = self.mn_tab.listen.wait(timeout=15) # 等待并获取一个数据包
|
||||
|
||||
datas = []
|
||||
if res:
|
||||
for data in res.response.body['data']["dataList"]:
|
||||
insert_data = {
|
||||
'id': int(data[4]),
|
||||
'open': float(data[3]),
|
||||
'high': float(data[1]),
|
||||
'low': float(data[2]),
|
||||
'close': float(data[0])
|
||||
}
|
||||
|
||||
datas.append(insert_data)
|
||||
|
||||
return datas
|
||||
except:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
def remove_tags_and_spaces(self,html):
|
||||
# 创建 BeautifulSoup 对象
|
||||
soup = BeautifulSoup(html, 'html.parser')
|
||||
# 获取去除标签后的文本
|
||||
text = soup.get_text()
|
||||
# 去除所有空格
|
||||
text = text.replace(" ", "")
|
||||
return text
|
||||
|
||||
def to_do_page(self):
|
||||
# self.page.get("https://www.weeaxs.site/zh-CN/futures/demo-trading/ETH-SUSDT")
|
||||
|
||||
self.mn_tab.ele('x://*[contains(text(), "市价")]', timeout=15).click()
|
||||
time.sleep(1)
|
||||
html_text = self.remove_tags_and_spaces(html=self.mn_tab.html)
|
||||
# 定义正则表达式模式,用于匹配包含逗号和小数点的数值
|
||||
pattern = r'委托可用([\d,]+\.\d+)SUSDT'
|
||||
|
||||
# 使用 re.search 方法查找匹配项
|
||||
match = re.search(pattern, html_text)
|
||||
number = ""
|
||||
if match:
|
||||
# 提取匹配到的数值字符串
|
||||
number_str = match.group(1).replace(',', '')
|
||||
# 将提取的字符串转换为浮点数
|
||||
number = float(number_str)
|
||||
|
||||
if not number:
|
||||
return
|
||||
|
||||
self.mn_tab.ele('x://input[@placeholder="请输入数量"]').input(number/10)
|
||||
time.sleep(1)
|
||||
|
||||
if self.direction == "long" and not self.start:
|
||||
logger.success(f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},开多")
|
||||
self.send_dingtalk_message(
|
||||
message_content=f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},开多")
|
||||
self.mn_tab.ele('x://*[contains(text(), "买入开多")]').click()
|
||||
self.start = 1
|
||||
elif self.direction == "short" and not self.start:
|
||||
logger.success(f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},开空")
|
||||
self.send_dingtalk_message(
|
||||
message_content=f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},开空")
|
||||
self.mn_tab.ele('x://*[contains(text(), "卖出开空")]').click()
|
||||
self.start = -1
|
||||
elif self.direction == "long" and self.start == -1:
|
||||
logger.success(f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},反手平空做多")
|
||||
self.send_dingtalk_message(
|
||||
message_content=f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},反手平空做多")
|
||||
self.mn_tab.ele('x://*[contains(text(), "闪电平仓")]').scroll.to_see(center=True)
|
||||
self.mn_tab.ele('x://*[contains(text(), "闪电平仓")]').click()
|
||||
time.sleep(3)
|
||||
self.mn_tab.ele('x://*[contains(text(), "买入开多")]').click()
|
||||
self.start = 1
|
||||
elif self.direction == "short" and self.start == 1:
|
||||
logger.success(f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},反手平多做空")
|
||||
self.send_dingtalk_message(
|
||||
message_content=f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},反手平多做空")
|
||||
self.mn_tab.ele('x://*[contains(text(), "闪电平仓")]').scroll.to_see(center=True)
|
||||
self.mn_tab.ele('x://*[contains(text(), "闪电平仓")]').click()
|
||||
time.sleep(3)
|
||||
self.mn_tab.ele('x://*[contains(text(), "卖出开空")]').click()
|
||||
self.start = -1
|
||||
|
||||
def get_text(self, target_text):
|
||||
# 去除目标文本中的空白字符
|
||||
cleaned_target_text = re.sub(r'\s', '', target_text)
|
||||
|
||||
# 创建 BeautifulSoup 对象
|
||||
soup = BeautifulSoup(self.mn_tab.html, 'html.parser')
|
||||
|
||||
# 遍历所有标签的文本内容
|
||||
for tag in soup.find_all():
|
||||
tag_text = tag.get_text()
|
||||
# 去除标签文本中的空白字符
|
||||
cleaned_tag_text = re.sub(r'\s', '', tag_text)
|
||||
if cleaned_target_text in cleaned_tag_text:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_now_time(self):
|
||||
# 获取当前时间戳
|
||||
current_timestamp = time.time()
|
||||
# 将当前时间戳转换为 datetime 对象
|
||||
current_datetime = datetime.datetime.fromtimestamp(current_timestamp)
|
||||
|
||||
# 计算距离当前时间最近的整点或 30 分时刻
|
||||
if current_datetime.minute < 30:
|
||||
target_datetime = current_datetime.replace(minute=0, second=0, microsecond=0)
|
||||
else:
|
||||
target_datetime = current_datetime.replace(minute=30, second=0, microsecond=0)
|
||||
|
||||
# 将目标 datetime 对象转换为时间戳
|
||||
target_timestamp = target_datetime.timestamp()
|
||||
|
||||
return int(target_timestamp) * 1000
|
||||
|
||||
def close_extra_tabs_in_browser(self):
|
||||
|
||||
try:
|
||||
for _, i in enumerate(self.page.get_tabs()):
|
||||
if _ == 0:
|
||||
continue
|
||||
|
||||
i.close()
|
||||
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
def action(self):
|
||||
# 获取比特端口
|
||||
if self.openBrowser():
|
||||
logger.info("获取打开比特成功,成功获取端口!!!")
|
||||
else:
|
||||
logger.error("打开比特失败!!!")
|
||||
return
|
||||
|
||||
# 接管浏览器
|
||||
if self.take_over_browser():
|
||||
logger.info("接管比特浏览器成功!!!")
|
||||
else:
|
||||
logger.error("接管浏览器失败!!!")
|
||||
return
|
||||
|
||||
if self.close_extra_tabs_in_browser():
|
||||
logger.info('关闭多余标签页成功!!!')
|
||||
else:
|
||||
logger.info('关闭多余标签页失败!!!')
|
||||
|
||||
self.mn_tab = self.page.new_tab()
|
||||
self.mn_tab.listen.start("public/quote/v1/getKlineV2")
|
||||
logger.success("浏览器开启抓包模式。。。")
|
||||
|
||||
self.mn_tab.get(url="https://www.weeaxs.site/zh-CN/futures/demo-trading/ETH-SUSDT") # 打开网页
|
||||
|
||||
self.pbar = tqdm(total=30, desc="等待时间中", ncols=80) # desc:进度条说明,ncols:长度
|
||||
|
||||
while True:
|
||||
# 获取当前时间
|
||||
current_time = time.localtime()
|
||||
current_minute = current_time.tm_min
|
||||
|
||||
if current_minute < 30:
|
||||
self.pbar.n = current_minute
|
||||
self.pbar.refresh()
|
||||
else:
|
||||
self.pbar.n = current_minute - 30
|
||||
self.pbar.refresh()
|
||||
|
||||
if current_minute not in [0, 1, 2, 3, 4, 5, 30, 31, 32, 33, 34, 58]: # 判断是否是 新的30分钟了
|
||||
time.sleep(10)
|
||||
continue
|
||||
|
||||
new_price_datas = self.get_price()
|
||||
if new_price_datas:
|
||||
logger.success("获取最新交易价格成功!!!")
|
||||
else:
|
||||
logger.info("获取最新价格有问题!!!")
|
||||
continue
|
||||
|
||||
new_price_datas1 = sorted(new_price_datas, key=lambda x: x["id"])
|
||||
self.kline_1, self.kline_2, self.kline_3 = new_price_datas1[-3:]
|
||||
|
||||
# 判断抓取的数据是否正确
|
||||
if self.get_now_time() != self.kline_3["id"]:
|
||||
continue
|
||||
|
||||
time.sleep(15)
|
||||
if self.get_text(target_text="仓位(1)"):
|
||||
if self.get_text(target_text="ETH/SUSDT多"):
|
||||
self.start = 1
|
||||
elif self.get_text(target_text="ETH/SUSDT空"):
|
||||
self.start = -1
|
||||
else:
|
||||
self.start = 0
|
||||
|
||||
if self.start == 1:
|
||||
if is_bearish(self.kline_1) and is_bearish(self.kline_2):
|
||||
logger.success(f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},平多")
|
||||
self.send_dingtalk_message(
|
||||
message_content=f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},平多")
|
||||
self.mn_tab.ele('x://*[contains(text(), "闪电平仓")]').scroll.to_see(center=True)
|
||||
self.mn_tab.ele('x://*[contains(text(), "闪电平仓")]').click()
|
||||
self.start = 0
|
||||
elif self.start == -1:
|
||||
if is_bullish(self.kline_1) and is_bullish(self.kline_2):
|
||||
logger.success(f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},平空")
|
||||
self.send_dingtalk_message(
|
||||
message_content=f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},平空")
|
||||
|
||||
self.mn_tab.ele('x://*[contains(text(), "闪电平仓")]').scroll.to_see(center=True)
|
||||
self.mn_tab.ele('x://*[contains(text(), "闪电平仓")]').click()
|
||||
self.start = 0
|
||||
|
||||
self.direction, signal_key = self.check_signal(prev=self.kline_1, curr=self.kline_2) # 判断信号
|
||||
|
||||
if self.direction:
|
||||
try:
|
||||
self.to_do_page()
|
||||
except Exception as e:
|
||||
self.send_dingtalk_message(
|
||||
message_content=f"{datetime.datetime.now()},{e}")
|
||||
|
||||
self.pbar.reset() # 重置进度条
|
||||
self.send_dingtalk_message(
|
||||
message_content=
|
||||
f"{datetime.datetime.now()},"
|
||||
f"目前有持仓:{"无" if self.start == 0 else ("多" if self.start == 1 else "空")},"
|
||||
f"当前信号:{"无" if not self.direction else ("多" if self.direction == "long" else "空")}"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
WeexTransaction(
|
||||
tge_id=146473,
|
||||
).action()
|
||||
|
||||
# //*[contains(text(), '特定文本')]
|
||||
@@ -1,408 +0,0 @@
|
||||
import re
|
||||
import json
|
||||
import hmac
|
||||
import time
|
||||
import base64
|
||||
import hashlib
|
||||
import datetime
|
||||
import requests
|
||||
|
||||
from tqdm import *
|
||||
from loguru import *
|
||||
from DrissionPage import *
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
|
||||
def is_bullish(c): # 阳线
|
||||
return float(c['close']) > float(c['open'])
|
||||
|
||||
|
||||
def is_bearish(c): # 阴线
|
||||
return float(c['close']) < float(c['open'])
|
||||
|
||||
|
||||
class WeexTransaction:
|
||||
def __init__(self, tge_id):
|
||||
self.tge_port = None # tge浏览器使用端口
|
||||
self.tge_id = tge_id # tge id
|
||||
self.tge_url = "http://127.0.0.1:50326" # tge本地服务url
|
||||
self.tge_headers = {
|
||||
"Authorization": f"Bearer asp_174003986c9b0799677c5b2c1adb76e402735d753bc91a91",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
# 替换为你自己的钉钉机器人 Webhook 地址
|
||||
self.webhook_url = "https://oapi.dingtalk.com/robot/send?access_token=e2fafb3f46866d50fe52cbb29650ba9ef1cbc97915dde238192f04c906fe4125"
|
||||
# 替换为你自己的钉钉机器人秘钥
|
||||
self.secret = "SEC5f320e72d7a4eaca540c66c3d09edff2f74936517390dee99ece6dd1b3611998"
|
||||
|
||||
self.page = None # 浏览器对象
|
||||
|
||||
self.start = 0 # 持仓状态 -1:做空,0:维持仓,1:做多
|
||||
self.kline_1 = None # 0:跌,1:涨
|
||||
self.kline_2 = None # 0:跌,1:涨
|
||||
|
||||
self.direction = None # 信号类型
|
||||
|
||||
self.pbar = None # 进度条对象
|
||||
|
||||
def get_signature(self, timestamp):
|
||||
# 将时间戳和密钥拼接
|
||||
string_to_sign = f'{timestamp}\n{self.secret}'
|
||||
string_to_sign = string_to_sign.encode('utf-8')
|
||||
# 使用 HMAC-SHA256 算法进行签名
|
||||
hmac_code = hmac.new(self.secret.encode('utf-8'), string_to_sign, digestmod=hashlib.sha256).digest()
|
||||
# 对签名结果进行 Base64 编码
|
||||
sign = base64.b64encode(hmac_code).decode('utf-8')
|
||||
return sign
|
||||
|
||||
# def send_dingtalk_message(self, message_content):
|
||||
# # 获取当前时间戳(毫秒)
|
||||
# timestamp = str(round(time.time() * 1000))
|
||||
# # 生成签名
|
||||
# sign = self.get_signature(timestamp, )
|
||||
# # 拼接带有签名信息的完整 Webhook URL
|
||||
# full_url = f"{self.webhook_url}×tamp={timestamp}&sign={sign}"
|
||||
#
|
||||
# # 定义消息内容
|
||||
# message = {
|
||||
# "msgtype": "text",
|
||||
# "text": {
|
||||
# "content": message_content
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# # 设置请求头
|
||||
# headers = {
|
||||
# "Content-Type": "application/json"
|
||||
# }
|
||||
#
|
||||
# try:
|
||||
# # 发送 POST 请求
|
||||
# response = requests.post(full_url, headers=headers, data=json.dumps(message))
|
||||
#
|
||||
# except requests.RequestException as e:
|
||||
# print(f"请求发生错误: {e}")
|
||||
|
||||
def send_dingtalk_message(self, message_content):
|
||||
|
||||
pass
|
||||
|
||||
# url = "http://8.137.99.82:9005/api/send_click?token=fegergauiernguie&phone=8613661496481"
|
||||
#
|
||||
# res = requests.post(
|
||||
# url=url,
|
||||
# json={
|
||||
# "phone": "8613661496481",
|
||||
# "bot_name": "ergggreef",
|
||||
# "datas": [
|
||||
# {"send_message": [message_content], "click_button": [""], },
|
||||
# ]
|
||||
#
|
||||
# }
|
||||
# )
|
||||
#
|
||||
# print(res.json())
|
||||
|
||||
def openBrowser(self, ): # 直接指定ID打开窗口,也可以使用 createBrowser 方法返回的ID
|
||||
|
||||
try:
|
||||
|
||||
response = requests.post(
|
||||
f"{self.tge_url}/api/browser/start",
|
||||
json={"envId": self.tge_id},
|
||||
headers=self.tge_headers
|
||||
)
|
||||
|
||||
self.tge_port = response.json()["data"]["port"]
|
||||
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def take_over_browser(self):
|
||||
try:
|
||||
co = ChromiumOptions()
|
||||
co.set_local_port(self.tge_port)
|
||||
|
||||
self.page = ChromiumPage(addr_or_opts=co)
|
||||
|
||||
self.page.set.window.max()
|
||||
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def is_bullish(self, c): # 阳线
|
||||
return float(c['close']) > float(c['open'])
|
||||
|
||||
def is_bearish(self, c): # 阴线
|
||||
return float(c['close']) < float(c['open'])
|
||||
|
||||
def check_signal(self, prev, curr):
|
||||
"""
|
||||
包住形态信号判定(仅15分钟K线):
|
||||
- 前跌后涨包住 -> 做多
|
||||
- 前涨后跌包住 -> 做空
|
||||
"""
|
||||
p_open, p_close = float(prev['open']), float(prev['close'])
|
||||
c_open, c_close = float(curr['open']), float(curr['close'])
|
||||
|
||||
# 前跌后涨包住 -> 做多
|
||||
if is_bullish(curr) and is_bearish(prev) and c_open <= p_close and c_close >= p_open:
|
||||
return "long", "bear_bull_engulf"
|
||||
|
||||
# 前涨后跌包住 -> 做空
|
||||
if is_bearish(curr) and is_bullish(prev) and c_open >= p_close and c_close <= p_open:
|
||||
return "short", "bull_bear_engulf"
|
||||
|
||||
return None, None
|
||||
|
||||
def get_price(self):
|
||||
|
||||
for i in range(3):
|
||||
try:
|
||||
logger.info(f"获取最新数据:{i + 1}次。。。")
|
||||
self.mn_tab.get(url="https://www.weeaxs.site/zh-CN/futures/ETH-USDT")
|
||||
res = self.mn_tab.listen.wait(timeout=15) # 等待并获取一个数据包
|
||||
|
||||
datas = []
|
||||
if res:
|
||||
for data in res.response.body['data']["dataList"]:
|
||||
insert_data = {
|
||||
'id': int(data[4]),
|
||||
'open': float(data[3]),
|
||||
'high': float(data[1]),
|
||||
'low': float(data[2]),
|
||||
'close': float(data[0])
|
||||
}
|
||||
|
||||
datas.append(insert_data)
|
||||
|
||||
return datas
|
||||
except:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
def remove_tags_and_spaces(self, html):
|
||||
# 创建 BeautifulSoup 对象
|
||||
soup = BeautifulSoup(html, 'html.parser')
|
||||
# 获取去除标签后的文本
|
||||
text = soup.get_text()
|
||||
# 去除所有空格
|
||||
text = text.replace(" ", "")
|
||||
return text
|
||||
|
||||
def to_do_page(self):
|
||||
# self.page.get("https://www.weeaxs.site/zh-CN/futures/ETH-USDT")
|
||||
|
||||
self.mn_tab.ele('x://*[contains(text(), "市价")]', timeout=15).click()
|
||||
time.sleep(1)
|
||||
html_text = self.remove_tags_and_spaces(html=self.mn_tab.html)
|
||||
# 使用 re.search 方法查找匹配项
|
||||
# match = re.search(r'委托可用\s*([0-9]+(?:\.[0-9]+)?)SUSDT', html_text)
|
||||
match = re.search(r"可用\s*([0-9,]+(?:\.[0-9]+)?)", html_text)
|
||||
number = ""
|
||||
if match:
|
||||
# 提取匹配到的数值字符串
|
||||
number_str = match.group(1).replace(',', '')
|
||||
# 将提取的字符串转换为浮点数
|
||||
number = float(number_str)
|
||||
|
||||
if not number:
|
||||
return
|
||||
|
||||
self.mn_tab.ele('x://input[@placeholder="请输入数量"]').input(number / 100)
|
||||
time.sleep(1)
|
||||
|
||||
if self.direction == "long" and not self.start:
|
||||
logger.success(f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},开多")
|
||||
self.send_dingtalk_message(
|
||||
message_content=f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},开多")
|
||||
self.mn_tab.ele('x://*[contains(text(), "买入开多")]').click()
|
||||
self.start = 1
|
||||
elif self.direction == "short" and not self.start:
|
||||
logger.success(f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},开空")
|
||||
self.send_dingtalk_message(
|
||||
message_content=f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},开空")
|
||||
self.mn_tab.ele('x://*[contains(text(), "卖出开空")]').click()
|
||||
self.start = -1
|
||||
elif self.direction == "long" and self.start == -1:
|
||||
logger.success(f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},反手平空做多")
|
||||
self.send_dingtalk_message(
|
||||
message_content=f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},反手平空做多")
|
||||
self.mn_tab.ele('x://*[contains(text(), "闪电平仓")]').scroll.to_see(center=True)
|
||||
self.mn_tab.ele('x://*[contains(text(), "闪电平仓")]').click()
|
||||
time.sleep(3)
|
||||
self.mn_tab.ele('x://*[contains(text(), "买入开多")]').click()
|
||||
self.start = 1
|
||||
elif self.direction == "short" and self.start == 1:
|
||||
logger.success(f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},反手平多做空")
|
||||
self.send_dingtalk_message(
|
||||
message_content=f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},反手平多做空")
|
||||
self.mn_tab.ele('x://*[contains(text(), "闪电平仓")]').scroll.to_see(center=True)
|
||||
self.mn_tab.ele('x://*[contains(text(), "闪电平仓")]').click()
|
||||
time.sleep(3)
|
||||
self.mn_tab.ele('x://*[contains(text(), "卖出开空")]').click()
|
||||
self.start = -1
|
||||
|
||||
def get_text(self, target_text):
|
||||
# 去除目标文本中的空白字符
|
||||
cleaned_target_text = re.sub(r'\s', '', target_text)
|
||||
|
||||
# 创建 BeautifulSoup 对象
|
||||
soup = BeautifulSoup(self.mn_tab.html, 'html.parser')
|
||||
|
||||
# 遍历所有标签的文本内容
|
||||
for tag in soup.find_all():
|
||||
tag_text = tag.get_text()
|
||||
# 去除标签文本中的空白字符
|
||||
cleaned_tag_text = re.sub(r'\s', '', tag_text)
|
||||
if cleaned_target_text in cleaned_tag_text:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_now_time(self):
|
||||
# 获取当前时间戳
|
||||
current_timestamp = time.time()
|
||||
# 将当前时间戳转换为 datetime 对象
|
||||
current_datetime = datetime.datetime.fromtimestamp(current_timestamp)
|
||||
|
||||
# 计算距离当前时间最近的整点或 30 分时刻
|
||||
if current_datetime.minute < 30:
|
||||
target_datetime = current_datetime.replace(minute=0, second=0, microsecond=0)
|
||||
else:
|
||||
target_datetime = current_datetime.replace(minute=30, second=0, microsecond=0)
|
||||
|
||||
# 将目标 datetime 对象转换为时间戳
|
||||
target_timestamp = target_datetime.timestamp()
|
||||
|
||||
return int(target_timestamp) * 1000
|
||||
|
||||
def close_extra_tabs_in_browser(self):
|
||||
|
||||
try:
|
||||
for _, i in enumerate(self.page.get_tabs()):
|
||||
if _ == 0:
|
||||
continue
|
||||
|
||||
i.close()
|
||||
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
|
||||
return False
|
||||
|
||||
def action(self):
|
||||
# 获取比特端口
|
||||
if self.openBrowser():
|
||||
logger.info("获取打开比特成功,成功获取端口!!!")
|
||||
else:
|
||||
logger.error("打开比特失败!!!")
|
||||
return
|
||||
|
||||
# 接管浏览器
|
||||
if self.take_over_browser():
|
||||
logger.info("接管比特浏览器成功!!!")
|
||||
else:
|
||||
logger.error("接管浏览器失败!!!")
|
||||
return
|
||||
|
||||
if self.close_extra_tabs_in_browser():
|
||||
logger.info('关闭多余标签页成功!!!')
|
||||
else:
|
||||
logger.info('关闭多余标签页失败!!!')
|
||||
|
||||
self.mn_tab = self.page.new_tab()
|
||||
self.mn_tab.listen.start("public/quote/v1/getKlineV2")
|
||||
logger.success("浏览器开启抓包模式。。。")
|
||||
|
||||
self.mn_tab.get(url="https://www.weeaxs.site/zh-CN/futures/ETH-USDT") # 打开网页
|
||||
|
||||
self.pbar = tqdm(total=30, desc="等待时间中", ncols=80) # desc:进度条说明,ncols:长度
|
||||
|
||||
while True:
|
||||
# 获取当前时间
|
||||
current_time = time.localtime()
|
||||
current_minute = current_time.tm_min
|
||||
|
||||
if current_minute < 30:
|
||||
self.pbar.n = current_minute
|
||||
self.pbar.refresh()
|
||||
else:
|
||||
self.pbar.n = current_minute - 30
|
||||
self.pbar.refresh()
|
||||
|
||||
# if current_minute not in range(60): # 判断是否是 新的30分钟了
|
||||
if current_minute not in [0, 1, 2, 3, 4, 5, 30, 31, 32, 33, 34, 58]: # 判断是否是 新的30分钟了
|
||||
time.sleep(10)
|
||||
continue
|
||||
|
||||
new_price_datas = self.get_price()
|
||||
if new_price_datas:
|
||||
logger.success("获取最新交易价格成功!!!")
|
||||
else:
|
||||
logger.info("获取最新价格有问题!!!")
|
||||
continue
|
||||
|
||||
new_price_datas1 = sorted(new_price_datas, key=lambda x: x["id"])
|
||||
self.kline_1, self.kline_2, self.kline_3 = new_price_datas1[-3:]
|
||||
|
||||
# 判断抓取的数据是否正确
|
||||
if self.get_now_time() != self.kline_3["id"]:
|
||||
continue
|
||||
|
||||
time.sleep(15)
|
||||
|
||||
if self.get_text(target_text="ETH/USDT多"):
|
||||
self.start = 1
|
||||
elif self.get_text(target_text="ETH/USDT空"):
|
||||
self.start = -1
|
||||
else:
|
||||
self.start = 0
|
||||
|
||||
if self.start == 1:
|
||||
if is_bearish(self.kline_1) and is_bearish(self.kline_2):
|
||||
logger.success(f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},平多")
|
||||
self.send_dingtalk_message(
|
||||
message_content=f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},平多")
|
||||
self.mn_tab.ele('x://*[contains(text(), "闪电平仓")]').scroll.to_see(center=True)
|
||||
self.mn_tab.ele('x://*[contains(text(), "闪电平仓")]').click()
|
||||
self.start = 0
|
||||
elif self.start == -1:
|
||||
if is_bullish(self.kline_1) and is_bullish(self.kline_2):
|
||||
logger.success(f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},平空")
|
||||
self.send_dingtalk_message(
|
||||
message_content=f"{datetime.datetime.now()},第一根信号:{self.kline_1},{self.kline_2},平空")
|
||||
|
||||
self.mn_tab.ele('x://*[contains(text(), "闪电平仓")]').scroll.to_see(center=True)
|
||||
self.mn_tab.ele('x://*[contains(text(), "闪电平仓")]').click()
|
||||
self.start = 0
|
||||
|
||||
self.direction, signal_key = self.check_signal(prev=self.kline_1, curr=self.kline_2) # 判断信号
|
||||
|
||||
if self.direction:
|
||||
try:
|
||||
self.to_do_page()
|
||||
except Exception as e:
|
||||
self.send_dingtalk_message(
|
||||
message_content=f"{datetime.datetime.now()},{e}")
|
||||
|
||||
self.pbar.reset() # 重置进度条
|
||||
self.send_dingtalk_message(
|
||||
message_content=
|
||||
f"{datetime.datetime.now()},"
|
||||
f"目前有持仓:{"无" if self.start == 0 else ("多" if self.start == 1 else "空")},"
|
||||
f"当前信号:{"无" if not self.direction else ("多" if self.direction == "long" else "空")}"
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
WeexTransaction(
|
||||
tge_id=146473,
|
||||
).action()
|
||||
|
||||
# //*[contains(text(), '特定文本')]
|
||||
@@ -1,15 +0,0 @@
|
||||
import requests
|
||||
|
||||
url = "http://8.137.99.82:9005/api/send_click?token=fegergauiernguie&phone=8613661496481"
|
||||
|
||||
res = requests.post(
|
||||
url=url,
|
||||
json={
|
||||
"phone": "8613661496481",
|
||||
"bot_name": "ergggreef",
|
||||
"datas": [
|
||||
{"send_message": ["grgegg"], "click_button": [""], },
|
||||
]
|
||||
|
||||
}
|
||||
)
|
||||
424
三分之一策略交易.py
Normal file
424
三分之一策略交易.py
Normal file
@@ -0,0 +1,424 @@
|
||||
import time
|
||||
|
||||
|
||||
from tqdm import tqdm
|
||||
from loguru import logger
|
||||
from bit_tools import openBrowser
|
||||
from DrissionPage import ChromiumPage
|
||||
from DrissionPage import ChromiumOptions
|
||||
|
||||
from bitmart.api_contract import APIContract
|
||||
|
||||
|
||||
class BitmartFuturesTransaction:
|
||||
def __init__(self, bit_id):
|
||||
|
||||
self.page: ChromiumPage | None = None
|
||||
|
||||
self.api_key = "a0fb7b98464fd9bcce67e7c519d58ec10d0c38a8"
|
||||
self.secret_key = "4eaeba78e77aeaab1c2027f846a276d164f264a44c2c1bb1c5f3be50c8de1ca5"
|
||||
self.memo = "合约交易"
|
||||
|
||||
self.contract_symbol = "ETHUSDT"
|
||||
|
||||
self.contractAPI = APIContract(self.api_key, self.secret_key, self.memo, timeout=(5, 15))
|
||||
|
||||
self.start = 0 # 持仓状态: -1 空, 0 无, 1 多
|
||||
self.direction = None
|
||||
|
||||
self.pbar = tqdm(total=30, desc="等待K线", ncols=80)
|
||||
|
||||
self.last_kline_time = None # 上一次处理的K线时间戳,用于判断是否是新K线
|
||||
|
||||
self.leverage = "100" # 高杠杆(全仓模式下可开更大仓位)
|
||||
self.open_type = "cross" # 全仓模式
|
||||
self.risk_percent = 0.01 # 每次开仓使用可用余额的 1%
|
||||
|
||||
self.open_avg_price = None # 开仓价格
|
||||
self.current_amount = None # 持仓量
|
||||
|
||||
self.bit_id = bit_id
|
||||
|
||||
# 策略相关变量
|
||||
self.prev_kline = None # 上一根K线
|
||||
self.current_kline = None # 当前K线
|
||||
self.prev_entity = None # 上一根K线实体大小
|
||||
self.current_open = None # 当前K线开盘价
|
||||
|
||||
def get_klines(self):
|
||||
"""获取最近2根K线(当前K线和上一根K线)"""
|
||||
try:
|
||||
end_time = int(time.time())
|
||||
# 获取足够多的条目确保有最新的K线
|
||||
response = self.contractAPI.get_kline(
|
||||
contract_symbol=self.contract_symbol,
|
||||
step=5, # 30分钟
|
||||
start_time=end_time - 3600 * 3, # 取最近3小时
|
||||
end_time=end_time
|
||||
)[0]["data"]
|
||||
|
||||
# 每根: [timestamp, open, high, low, close, volume]
|
||||
formatted = []
|
||||
for k in response:
|
||||
formatted.append({
|
||||
'id': int(k["timestamp"]),
|
||||
'open': float(k["open_price"]),
|
||||
'high': float(k["high_price"]),
|
||||
'low': float(k["low_price"]),
|
||||
'close': float(k["close_price"])
|
||||
})
|
||||
formatted.sort(key=lambda x: x['id'])
|
||||
|
||||
# 返回最近2根K线:倒数第二根(上一根)和最后一根(当前)
|
||||
if len(formatted) >= 2:
|
||||
return formatted[-2], formatted[-1]
|
||||
return None, None
|
||||
except Exception as e:
|
||||
logger.error(f"获取K线异常: {e}")
|
||||
self.ding(text="获取K线异常", error=True)
|
||||
return None, None
|
||||
|
||||
def get_current_price(self):
|
||||
"""获取当前最新价格"""
|
||||
try:
|
||||
end_time = int(time.time())
|
||||
response = self.contractAPI.get_kline(
|
||||
contract_symbol=self.contract_symbol,
|
||||
step=1, # 1分钟
|
||||
start_time=end_time - 3600 * 1, # 取最近1小时
|
||||
end_time=end_time
|
||||
)[0]
|
||||
if response['code'] == 1000:
|
||||
return float(response['data'][-1]["close_price"])
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"获取价格异常: {e}")
|
||||
return None
|
||||
|
||||
def get_available_balance(self):
|
||||
"""获取合约账户可用USDT余额"""
|
||||
try:
|
||||
response = self.contractAPI.get_assets_detail()[0]
|
||||
if response['code'] == 1000:
|
||||
data = response['data']
|
||||
if isinstance(data, dict):
|
||||
return float(data.get('available_balance', 0))
|
||||
elif isinstance(data, list):
|
||||
for asset in data:
|
||||
if asset.get('currency') == 'USDT':
|
||||
return float(asset.get('available_balance', 0))
|
||||
return None
|
||||
except Exception as e:
|
||||
logger.error(f"余额查询异常: {e}")
|
||||
return None
|
||||
|
||||
def get_position_status(self):
|
||||
"""获取当前持仓方向"""
|
||||
try:
|
||||
response = self.contractAPI.get_position(contract_symbol=self.contract_symbol)[0]
|
||||
if response['code'] == 1000:
|
||||
positions = response['data']
|
||||
if not positions:
|
||||
self.start = 0
|
||||
return True
|
||||
self.start = 1 if positions[0]['position_type'] == 1 else -1
|
||||
self.open_avg_price = float(positions[0]['open_avg_price'])
|
||||
self.current_amount = positions[0]['current_amount']
|
||||
self.position_cross = positions[0]["position_cross"]
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"持仓查询异常: {e}")
|
||||
return False
|
||||
|
||||
def set_leverage(self):
|
||||
"""程序启动时设置全仓 + 高杠杆"""
|
||||
try:
|
||||
response = self.contractAPI.post_submit_leverage(
|
||||
contract_symbol=self.contract_symbol,
|
||||
leverage=self.leverage,
|
||||
open_type=self.open_type
|
||||
)[0]
|
||||
if response['code'] == 1000:
|
||||
logger.success(f"全仓模式 + {self.leverage}x 杠杆设置成功")
|
||||
return True
|
||||
else:
|
||||
logger.error(f"杠杆设置失败: {response}")
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"设置杠杆异常: {e}")
|
||||
return False
|
||||
|
||||
def openBrowser(self):
|
||||
"""打开 TGE 对应浏览器实例"""
|
||||
try:
|
||||
bit_port = openBrowser(id=self.bit_id)
|
||||
co = ChromiumOptions()
|
||||
co.set_local_port(port=bit_port)
|
||||
self.page = ChromiumPage(addr_or_opts=co)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def click_safe(self, xpath, sleep=0.5):
|
||||
"""安全点击"""
|
||||
try:
|
||||
ele = self.page.ele(xpath)
|
||||
if not ele:
|
||||
return False
|
||||
ele.scroll.to_see(center=True)
|
||||
time.sleep(sleep)
|
||||
ele.click()
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def 平仓(self):
|
||||
"""平仓操作"""
|
||||
self.click_safe('x://span[normalize-space(text()) ="市价"]')
|
||||
|
||||
def 开单(self, marketPriceLongOrder=0, limitPriceShortOrder=0, size=None, price=None):
|
||||
"""
|
||||
marketPriceLongOrder 市价做多或者做空,1是做多,-1是做空
|
||||
limitPriceShortOrder 限价做多或者做空
|
||||
"""
|
||||
if marketPriceLongOrder == -1:
|
||||
self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
self.page.ele('x://*[@id="size_0"]').input(size)
|
||||
self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]')
|
||||
elif marketPriceLongOrder == 1:
|
||||
self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
self.page.ele('x://*[@id="size_0"]').input(size)
|
||||
self.click_safe('x://span[normalize-space(text()) ="买入/做多"]')
|
||||
|
||||
if limitPriceShortOrder == -1:
|
||||
self.click_safe('x://button[normalize-space(text()) ="限价"]')
|
||||
self.page.ele('x://*[@id="price_0"]').input(vals=price, clear=True)
|
||||
time.sleep(1)
|
||||
self.page.ele('x://*[@id="size_0"]').input(1)
|
||||
self.click_safe('x://span[normalize-space(text()) ="卖出/做空"]')
|
||||
elif limitPriceShortOrder == 1:
|
||||
self.click_safe('x://button[normalize-space(text()) ="限价"]')
|
||||
self.page.ele('x://*[@id="price_0"]').input(vals=price, clear=True)
|
||||
time.sleep(1)
|
||||
self.page.ele('x://*[@id="size_0"]').input(1)
|
||||
self.click_safe('x://span[normalize-space(text()) ="买入/做多"]')
|
||||
|
||||
def ding(self, text, error=False):
|
||||
"""日志通知"""
|
||||
if error:
|
||||
logger.error(text)
|
||||
else:
|
||||
logger.info(text)
|
||||
|
||||
def calculate_entity(self, kline):
|
||||
"""计算K线实体大小(绝对值)"""
|
||||
return abs(kline['close'] - kline['open'])
|
||||
|
||||
def calculate_upper_shadow(self, kline):
|
||||
"""计算上阴线(上影线)涨幅百分比"""
|
||||
# 上阴线 = (最高价 - max(开盘价, 收盘价)) / max(开盘价, 收盘价)
|
||||
body_top = max(kline['open'], kline['close'])
|
||||
if body_top == 0:
|
||||
return 0
|
||||
return (kline['high'] - body_top) / body_top * 100
|
||||
|
||||
def calculate_lower_shadow(self, kline):
|
||||
"""计算下阴线(下影线)跌幅百分比"""
|
||||
# 下阴线 = (min(开盘价, 收盘价) - 最低价) / min(开盘价, 收盘价)
|
||||
body_bottom = min(kline['open'], kline['close'])
|
||||
if body_bottom == 0:
|
||||
return 0
|
||||
return (body_bottom - kline['low']) / body_bottom * 100
|
||||
|
||||
def get_entity_edge(self, kline):
|
||||
"""获取K线实体边(收盘价或开盘价,取决于是阳线还是阴线)"""
|
||||
# 阳线(收盘>开盘):实体上边=收盘价,实体下边=开盘价
|
||||
# 阴线(收盘<开盘):实体上边=开盘价,实体下边=收盘价
|
||||
return {
|
||||
'upper': max(kline['open'], kline['close']), # 实体上边
|
||||
'lower': min(kline['open'], kline['close']) # 实体下边
|
||||
}
|
||||
|
||||
def check_signal(self, current_price, prev_kline, current_kline):
|
||||
"""
|
||||
检查交易信号
|
||||
返回: ('long', trigger_price) / ('short', trigger_price) / None
|
||||
"""
|
||||
# 计算上一根K线实体
|
||||
prev_entity = self.calculate_entity(prev_kline)
|
||||
|
||||
# 实体过小不交易(实体 < 0.1)
|
||||
if prev_entity < 0.1:
|
||||
logger.info(f"上一根K线实体过小: {prev_entity:.4f},跳过信号检测")
|
||||
return None
|
||||
|
||||
# 获取上一根K线的实体上下边
|
||||
prev_entity_edge = self.get_entity_edge(prev_kline)
|
||||
prev_entity_upper = prev_entity_edge['upper'] # 实体上边
|
||||
prev_entity_lower = prev_entity_edge['lower'] # 实体下边
|
||||
|
||||
# 计算触发价(基于上一根K线实体位置)
|
||||
long_trigger = prev_entity_lower + prev_entity / 3 # 做多触发价 = 实体下边 + 实体/3(下三分之一处)
|
||||
short_trigger = prev_entity_upper - prev_entity / 3 # 做空触发价 = 实体上边 - 实体/3(上三分之一处)
|
||||
|
||||
logger.info(f"当前价格: {current_price:.2f}, 上一根实体: {prev_entity:.4f}")
|
||||
logger.info(f"上一根实体上边: {prev_entity_upper:.2f}, 下边: {prev_entity_lower:.2f}")
|
||||
logger.info(f"做多触发价(下1/3): {long_trigger:.2f}, 做空触发价(上1/3): {short_trigger:.2f}")
|
||||
|
||||
# 无持仓时检查开仓信号
|
||||
if self.start == 0:
|
||||
if current_price >= long_trigger:
|
||||
logger.info(f"触发做多信号!价格 {current_price:.2f} >= 触发价(下1/3) {long_trigger:.2f}")
|
||||
return ('long', long_trigger)
|
||||
elif current_price <= short_trigger:
|
||||
logger.info(f"触发做空信号!价格 {current_price:.2f} <= 触发价(上1/3) {short_trigger:.2f}")
|
||||
return ('short', short_trigger)
|
||||
|
||||
# 持仓时检查反手信号
|
||||
elif self.start == 1: # 持多仓
|
||||
# 反手条件1: 价格跌到上一根K线的上三分之一处(做空触发价)
|
||||
if current_price <= short_trigger:
|
||||
logger.info(f"持多反手做空!价格 {current_price:.2f} <= 触发价(上1/3) {short_trigger:.2f}")
|
||||
return ('reverse_short', short_trigger)
|
||||
|
||||
# 反手条件2: 上一根K线上阴线涨幅>0.01%,当前跌到上一根实体下边
|
||||
upper_shadow_pct = self.calculate_upper_shadow(prev_kline)
|
||||
if upper_shadow_pct > 0.01 and current_price <= prev_entity_lower:
|
||||
logger.info(f"持多反手做空!上阴线涨幅 {upper_shadow_pct:.4f}% > 0.01%,"
|
||||
f"价格 {current_price:.2f} <= 实体下边 {prev_entity_lower:.2f}")
|
||||
return ('reverse_short', prev_entity_lower)
|
||||
|
||||
elif self.start == -1: # 持空仓
|
||||
# 反手条件1: 价格涨到上一根K线的下三分之一处(做多触发价)
|
||||
if current_price >= long_trigger:
|
||||
logger.info(f"持空反手做多!价格 {current_price:.2f} >= 触发价(下1/3) {long_trigger:.2f}")
|
||||
return ('reverse_long', long_trigger)
|
||||
|
||||
# 反手条件2: 上一根K线下阴线跌幅>0.01%,当前涨到上一根实体上边
|
||||
lower_shadow_pct = self.calculate_lower_shadow(prev_kline)
|
||||
if lower_shadow_pct > 0.01 and current_price >= prev_entity_upper:
|
||||
logger.info(f"持空反手做多!下阴线跌幅 {lower_shadow_pct:.4f}% > 0.01%,"
|
||||
f"价格 {current_price:.2f} >= 实体上边 {prev_entity_upper:.2f}")
|
||||
return ('reverse_long', prev_entity_upper)
|
||||
|
||||
return None
|
||||
|
||||
def execute_trade(self, signal, size=1):
|
||||
"""执行交易"""
|
||||
signal_type, trigger_price = signal
|
||||
size= 25
|
||||
if signal_type == 'long':
|
||||
# 开多
|
||||
logger.info(f"执行开多,触发价: {trigger_price:.2f}")
|
||||
self.开单(marketPriceLongOrder=1, size=size)
|
||||
self.start = 1
|
||||
|
||||
elif signal_type == 'short':
|
||||
# 开空
|
||||
logger.info(f"执行开空,触发价: {trigger_price:.2f}")
|
||||
self.开单(marketPriceLongOrder=-1, size=size)
|
||||
self.start = -1
|
||||
|
||||
elif signal_type == 'reverse_long':
|
||||
# 平空 + 开多(反手做多)
|
||||
logger.info(f"执行反手做多,触发价: {trigger_price:.2f}")
|
||||
self.平仓()
|
||||
time.sleep(1)
|
||||
self.开单(marketPriceLongOrder=1, size=size)
|
||||
self.start = 1
|
||||
|
||||
elif signal_type == 'reverse_short':
|
||||
# 平多 + 开空(反手做空)
|
||||
logger.info(f"执行反手做空,触发价: {trigger_price:.2f}")
|
||||
self.平仓()
|
||||
time.sleep(1)
|
||||
self.开单(marketPriceLongOrder=-1, size=size)
|
||||
self.start = -1
|
||||
|
||||
def action(self):
|
||||
"""主循环"""
|
||||
# 启动时设置全仓高杠杆
|
||||
if not self.set_leverage():
|
||||
logger.error("杠杆设置失败,程序继续运行但可能下单失败")
|
||||
return
|
||||
|
||||
# 打开浏览器
|
||||
if not self.openBrowser():
|
||||
self.ding("打开浏览器失败!", error=True)
|
||||
return
|
||||
logger.info("浏览器打开成功")
|
||||
|
||||
# 进入交易页面
|
||||
self.page.get("https://derivatives.bitmart.com/zh-CN/futures/ETHUSDT")
|
||||
time.sleep(3)
|
||||
|
||||
logger.info("开始运行三分之一策略交易...")
|
||||
|
||||
# 标记是否刚执行过交易(用于跳过从交易所获取持仓状态,避免延迟问题)
|
||||
just_traded = False
|
||||
|
||||
while True:
|
||||
try:
|
||||
# 1. 获取K线数据(当前K线和上一根K线)
|
||||
prev_kline, current_kline = self.get_klines()
|
||||
if not prev_kline or not current_kline:
|
||||
logger.warning("获取K线失败,等待重试...")
|
||||
time.sleep(5)
|
||||
continue
|
||||
|
||||
# 2. 获取当前价格
|
||||
current_price = self.get_current_price()
|
||||
if not current_price:
|
||||
logger.warning("获取价格失败,等待重试...")
|
||||
time.sleep(2)
|
||||
continue
|
||||
|
||||
# 3. 获取持仓状态(如果刚交易过,信任本地状态,跳过API查询避免延迟)
|
||||
if not just_traded:
|
||||
if not self.get_position_status():
|
||||
logger.warning("获取持仓状态失败,等待重试...")
|
||||
time.sleep(2)
|
||||
continue
|
||||
else:
|
||||
logger.info(f"刚执行交易,信任本地持仓状态: {self.start}")
|
||||
just_traded = False # 重置标记
|
||||
|
||||
# 4. 检查信号
|
||||
signal = self.check_signal(current_price, prev_kline, current_kline)
|
||||
|
||||
# 5. 有信号则执行交易
|
||||
if signal:
|
||||
self.execute_trade(signal, size=1)
|
||||
logger.success(f"交易执行完成: {signal[0]}, 当前持仓状态: {self.start}")
|
||||
just_traded = True # 标记刚执行过交易
|
||||
|
||||
# 交易后立即再次检查是否有反手信号(同一根K线内可能多次反手)
|
||||
time.sleep(1) # 短暂等待
|
||||
|
||||
# 重新获取价格,检查是否需要再次反手
|
||||
new_price = self.get_current_price()
|
||||
if new_price:
|
||||
new_signal = self.check_signal(new_price, prev_kline, current_kline)
|
||||
if new_signal:
|
||||
logger.info(f"检测到连续反手信号!当前价格: {new_price:.2f}")
|
||||
self.execute_trade(new_signal, size=1)
|
||||
logger.success(f"连续反手执行完成: {new_signal[0]}, 当前持仓状态: {self.start}")
|
||||
|
||||
time.sleep(1) # 交易后稍等
|
||||
continue # 立即进入下一次循环继续监控
|
||||
|
||||
# 6. 短暂等待后继续循环(同一根K线遇到信号就操作)
|
||||
time.sleep(3)
|
||||
|
||||
except KeyboardInterrupt:
|
||||
logger.info("用户中断,程序退出")
|
||||
break
|
||||
except Exception as e:
|
||||
logger.error(f"主循环异常: {e}")
|
||||
time.sleep(5)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
BitmartFuturesTransaction(bit_id="f2320f57e24c45529a009e1541e25961").action()
|
||||
@@ -1,23 +1,24 @@
|
||||
"""
|
||||
BitMart 五分之一回归策略交易(精准版)
|
||||
使用3分钟K线周期计算触发价格,实时监测;同根K线内多空都触及时用1分钟K线判断先后
|
||||
BitMart 五分之一策略交易(3分钟精准版)
|
||||
使用3分钟K线周期计算触发价格,实时监测;同根K线内多空都触及时用开盘价距离判断先后
|
||||
|
||||
策略规则(与 bitmart/回测-三分之一策略-精准版.py 一致):
|
||||
1. 触发价格计算(基于有效的前一根K线,实体>=0.1):
|
||||
策略规则(与 bitmart/回测数据-五分之一策略-3分钟精准版.py 完全一致):
|
||||
1. 触发价格计算(基于有效的前一根3分钟K线,实体>=0.1):
|
||||
- 做多触发价格 = 收盘价 + 实体/5(从收盘价往上涨1/5)
|
||||
- 做空触发价格 = 收盘价 - 实体/5(从收盘价往下跌1/5)
|
||||
|
||||
2. 信号触发条件:
|
||||
- 当前K线最高价 >= 做多触发价格 → 做多信号
|
||||
- 当前K线最低价 <= 做空触发价格 → 做空信号
|
||||
- 当前3分钟K线最高价 >= 做多触发价格 → 做多信号
|
||||
- 当前3分钟K线最低价 <= 做空触发价格 → 做空信号
|
||||
|
||||
3. 执行逻辑:
|
||||
- 做多时遇到做空信号 -> 平多并反手开空
|
||||
- 做空时遇到做多信号 -> 平空并反手开多
|
||||
- 同一根3分钟K线内只交易一次
|
||||
|
||||
4. 精准判断(使用1分钟K线):
|
||||
- 当当前3分钟K线同时触及做多和做空价格时,拉取该3分钟对应的3根1分钟K线判断哪个方向先被触发
|
||||
4. 同根触发判断(无需1分钟K线):
|
||||
- 当一根3分钟K线同时触及做多和做空价格时
|
||||
- 使用该3分钟K线开盘价与触发价的距离判断先后
|
||||
"""
|
||||
import random
|
||||
import time
|
||||
@@ -71,18 +72,6 @@ class BitmartOneFifthStrategy:
|
||||
self.last_trigger_kline_id = None
|
||||
self.last_trigger_direction = None
|
||||
self.last_trade_kline_id = None
|
||||
|
||||
# 反手信号当前价格容差(美元)
|
||||
# 当检测到反手信号时,当前价格必须在触发价附近才执行
|
||||
# 避免"先涨后跌"或"先跌后涨"的情况下错误开仓
|
||||
self.reverse_price_tolerance = 2.0 # 2美元容差
|
||||
|
||||
# 基于开仓价格的反手信号参数
|
||||
# 记录开仓时使用的前一根K线实体大小(用于计算反手触发价)
|
||||
self.entry_prev_body = None # 开仓时前一根K线的实体大小
|
||||
self.entry_price = None # 开仓价格(用于计算反手触发价)
|
||||
self.entry_kline_id = None # 开仓时的K线ID(用于判断是否在同一根K线内)
|
||||
self.first_minute_reverse_executed = False # 当前K线是否已执行过第一分钟反手
|
||||
|
||||
# ========================= 五分之一策略核心 =========================
|
||||
|
||||
@@ -118,156 +107,13 @@ class BitmartOneFifthStrategy:
|
||||
short_trigger = p_close - body / 5
|
||||
return long_trigger, short_trigger
|
||||
|
||||
def get_1m_bars_for_3m_bar(self, bar_3m):
|
||||
"""获取当前3分钟K线对应的3根1分钟K线(用于同根内多空都触发时判断先后)"""
|
||||
try:
|
||||
start_ts = int(bar_3m['id'])
|
||||
end_ts = start_ts + 3 * 60 # 秒
|
||||
response = self.contractAPI.get_kline(
|
||||
contract_symbol=self.contract_symbol,
|
||||
step=1,
|
||||
start_time=start_ts,
|
||||
end_time=end_ts
|
||||
)[0]
|
||||
if response.get('code') != 1000:
|
||||
return []
|
||||
data = response.get('data', [])
|
||||
out = []
|
||||
for k in data:
|
||||
out.append({
|
||||
'id': int(k["timestamp"]),
|
||||
'open': float(k["open_price"]),
|
||||
'high': float(k["high_price"]),
|
||||
'low': float(k["low_price"]),
|
||||
'close': float(k["close_price"])
|
||||
})
|
||||
out.sort(key=lambda x: x['id'])
|
||||
return out
|
||||
except Exception as e:
|
||||
logger.warning(f"获取1分钟K线失败: {e}")
|
||||
return []
|
||||
|
||||
def determine_trigger_order_by_1m(self, bars_1m, long_trigger, short_trigger):
|
||||
def check_realtime_trigger(self, kline_data):
|
||||
"""
|
||||
用1分钟K线判断在3分钟周期内先触发做多还是做空
|
||||
返回 'long', 'short' 或 None
|
||||
"""
|
||||
if not bars_1m:
|
||||
return None
|
||||
for bar in bars_1m:
|
||||
high = float(bar['high'])
|
||||
low = float(bar['low'])
|
||||
open_price = float(bar['open'])
|
||||
long_ok = high >= long_trigger
|
||||
short_ok = low <= short_trigger
|
||||
if long_ok and not short_ok:
|
||||
return 'long'
|
||||
if short_ok and not long_ok:
|
||||
return 'short'
|
||||
if long_ok and short_ok:
|
||||
d_long = abs(long_trigger - open_price)
|
||||
d_short = abs(short_trigger - open_price)
|
||||
return 'short' if d_short < d_long else 'long'
|
||||
return None
|
||||
|
||||
def check_first_minute_reverse_signal(self, curr_kline, kline_data):
|
||||
"""
|
||||
检测基于开仓价格的反手信号(只在3分钟K线的第一分钟有效)
|
||||
|
||||
反手规则:
|
||||
- 空仓反手开多:开空仓后,价格涨到 开仓价格 + 前一根K线实体/5 → 平空开多
|
||||
- 多仓反手开空:开多仓后,价格跌到 开仓价格 - 前一根K线实体/5 → 平多开空
|
||||
|
||||
:param curr_kline: 当前3分钟K线数据
|
||||
:param kline_data: 所有K线数据(用于获取前一根K线实体)
|
||||
:return: (方向, 触发价格) 或 (None, None)
|
||||
"""
|
||||
# 检查是否有持仓
|
||||
if self.start == 0:
|
||||
return None, None
|
||||
|
||||
curr_kline_id = curr_kline['id']
|
||||
|
||||
# 如果K线切换了,重置第一分钟反手标记
|
||||
if self.entry_kline_id != curr_kline_id:
|
||||
self.first_minute_reverse_executed = False
|
||||
self.entry_kline_id = curr_kline_id # 更新当前K线ID
|
||||
|
||||
# 检查当前K线是否已执行过第一分钟反手
|
||||
if self.first_minute_reverse_executed:
|
||||
return None, None
|
||||
|
||||
# 获取开仓价格(如果没有记录,使用API返回的开仓均价)
|
||||
entry_price = self.entry_price
|
||||
if entry_price is None and self.open_avg_price:
|
||||
entry_price = float(self.open_avg_price)
|
||||
if entry_price is None:
|
||||
return None, None
|
||||
|
||||
# 获取前一根有效K线的实体大小
|
||||
valid_prev_idx, valid_prev = self.find_valid_prev_bar(kline_data, len(kline_data) - 1, self.min_body_size)
|
||||
if valid_prev is None:
|
||||
return None, None
|
||||
prev_body = self.get_body_size(valid_prev)
|
||||
|
||||
# 计算反手触发价格
|
||||
reverse_offset = prev_body / 5
|
||||
|
||||
# 获取第一分钟K线
|
||||
bars_1m = self.get_1m_bars_for_3m_bar(curr_kline)
|
||||
if not bars_1m or len(bars_1m) < 1:
|
||||
return None, None
|
||||
|
||||
first_1m = bars_1m[0] # 第一分钟K线
|
||||
first_1m_high = float(first_1m['high'])
|
||||
first_1m_low = float(first_1m['low'])
|
||||
first_1m_close = float(first_1m['close'])
|
||||
|
||||
if self.start == -1:
|
||||
# 持有空仓,检测反手开多信号
|
||||
# 反手触发价 = 开仓价格 + 前一根实体/5
|
||||
reverse_long_trigger = entry_price + reverse_offset
|
||||
|
||||
# 检查第一分钟是否触及反手触发价
|
||||
if first_1m_high >= reverse_long_trigger:
|
||||
# 第一分钟高点触及反手触发价,并且当前价格在触发价附近
|
||||
if first_1m_close >= reverse_long_trigger - self.reverse_price_tolerance:
|
||||
logger.info(f"🔄 第一分钟反手信号检测:持空仓,第一分钟高点{first_1m_high:.2f}>=反手触发价{reverse_long_trigger:.2f}")
|
||||
logger.info(f" 开仓价={entry_price:.2f}, 前一根实体={prev_body:.2f}, 实体/5={reverse_offset:.2f}")
|
||||
return 'long', reverse_long_trigger
|
||||
else:
|
||||
logger.debug(f"第一分钟反手信号被过滤:当前价格{first_1m_close:.2f}已远离触发价{reverse_long_trigger:.2f}")
|
||||
|
||||
elif self.start == 1:
|
||||
# 持有多仓,检测反手开空信号
|
||||
# 反手触发价 = 开仓价格 - 前一根实体/5
|
||||
reverse_short_trigger = entry_price - reverse_offset
|
||||
|
||||
# 检查第一分钟是否触及反手触发价
|
||||
if first_1m_low <= reverse_short_trigger:
|
||||
# 第一分钟低点触及反手触发价,并且当前价格在触发价附近
|
||||
if first_1m_close <= reverse_short_trigger + self.reverse_price_tolerance:
|
||||
logger.info(f"🔄 第一分钟反手信号检测:持多仓,第一分钟低点{first_1m_low:.2f}<=反手触发价{reverse_short_trigger:.2f}")
|
||||
logger.info(f" 开仓价={entry_price:.2f}, 前一根实体={prev_body:.2f}, 实体/5={reverse_offset:.2f}")
|
||||
return 'short', reverse_short_trigger
|
||||
else:
|
||||
logger.debug(f"第一分钟反手信号被过滤:当前价格{first_1m_close:.2f}已远离触发价{reverse_short_trigger:.2f}")
|
||||
|
||||
return None, None
|
||||
|
||||
def check_realtime_trigger(self, kline_data, current_position=0):
|
||||
"""
|
||||
实时检测当前3分钟K线是否触发信号
|
||||
检查当前3分钟K线是否触发交易信号(与回测逻辑完全一致)
|
||||
若同时触发多空,则用开盘价距离判断先后顺序(不请求1分钟K线)。
|
||||
|
||||
参数:
|
||||
kline_data: K线数据列表
|
||||
current_position: 当前持仓状态 (1=多, -1=空, 0=无)
|
||||
|
||||
逻辑优化:
|
||||
- 当已有持仓时,只关心反向信号(有多仓只看空信号,有空仓只看多信号)
|
||||
- 无仓位时,用1分钟K线判断先触发的方向
|
||||
- 【重要】反手信号不仅要求K线高/低点触及触发价,还要求当前价格在触发价附近
|
||||
避免"先涨后跌"或"先跌后涨"的情况下错误开仓
|
||||
|
||||
返回:(方向, 触发价格, 有效前一根K线, 当前K线) 或 (None, None, None, None)
|
||||
"""
|
||||
@@ -288,60 +134,35 @@ class BitmartOneFifthStrategy:
|
||||
|
||||
c_high = float(curr['high'])
|
||||
c_low = float(curr['low'])
|
||||
c_close = float(curr['close']) # 当前价格(用于检查价格是否仍在触发价附近)
|
||||
|
||||
long_triggered = c_high >= long_trigger
|
||||
short_triggered = c_low <= short_trigger
|
||||
both_triggered = long_triggered and short_triggered
|
||||
|
||||
direction = None
|
||||
trigger_price = None
|
||||
|
||||
# 关键优化:根据当前持仓状态决定关注哪个方向的信号
|
||||
if current_position == 1:
|
||||
# 当前是多仓,只关心空信号(用于平多开空)
|
||||
if short_triggered:
|
||||
# 【重要】额外检查:当前价格必须在做空触发价附近或下方
|
||||
# 如果价格已经涨回去了(当前价格远高于做空触发价),则不触发
|
||||
if c_close <= short_trigger + self.reverse_price_tolerance:
|
||||
direction = 'short'
|
||||
trigger_price = short_trigger
|
||||
else:
|
||||
logger.debug(f"空信号被过滤:当前价格{c_close:.2f}已远离做空触发价{short_trigger:.2f}(容差{self.reverse_price_tolerance})")
|
||||
|
||||
elif current_position == -1:
|
||||
# 当前是空仓,只关心多信号(用于平空开多)
|
||||
if long_triggered:
|
||||
# 【重要】额外检查:当前价格必须在做多触发价附近或上方
|
||||
# 如果价格已经跌回去了(当前价格远低于做多触发价),则不触发
|
||||
if c_close >= long_trigger - self.reverse_price_tolerance:
|
||||
direction = 'long'
|
||||
trigger_price = long_trigger
|
||||
else:
|
||||
logger.debug(f"多信号被过滤:当前价格{c_close:.2f}已远离做多触发价{long_trigger:.2f}(容差{self.reverse_price_tolerance})")
|
||||
else:
|
||||
# 无仓位,按原逻辑判断先触发的方向
|
||||
if long_triggered and short_triggered:
|
||||
bars_1m = self.get_1m_bars_for_3m_bar(curr)
|
||||
if bars_1m:
|
||||
direction = self.determine_trigger_order_by_1m(
|
||||
bars_1m, long_trigger, short_trigger
|
||||
)
|
||||
trigger_price = long_trigger if direction == 'long' else short_trigger
|
||||
if direction is None:
|
||||
c_open = float(curr['open'])
|
||||
d_long = abs(long_trigger - c_open)
|
||||
d_short = abs(short_trigger - c_open)
|
||||
direction = 'short' if d_short <= d_long else 'long'
|
||||
trigger_price = long_trigger if direction == 'long' else short_trigger
|
||||
elif short_triggered:
|
||||
# 如果同时触发多空,用开盘价距离判断先后顺序(避免请求1分钟K线)
|
||||
if both_triggered:
|
||||
c_open = float(curr['open'])
|
||||
dist_to_long = abs(long_trigger - c_open)
|
||||
dist_to_short = abs(short_trigger - c_open)
|
||||
if dist_to_short <= dist_to_long:
|
||||
direction = 'short'
|
||||
trigger_price = short_trigger
|
||||
elif long_triggered:
|
||||
else:
|
||||
direction = 'long'
|
||||
trigger_price = long_trigger
|
||||
elif short_triggered:
|
||||
direction = 'short'
|
||||
trigger_price = short_trigger
|
||||
elif long_triggered:
|
||||
direction = 'long'
|
||||
trigger_price = long_trigger
|
||||
|
||||
if direction is None:
|
||||
return None, None, None, None
|
||||
|
||||
# 避免同一根K线内重复触发相同信号
|
||||
if self.last_trigger_kline_id == curr_kline_id and self.last_trigger_direction == direction:
|
||||
return None, None, None, None
|
||||
|
||||
@@ -494,7 +315,6 @@ class BitmartOneFifthStrategy:
|
||||
return False
|
||||
direction_str = "做多" if marketPriceLongOrder == 1 else "做空"
|
||||
logger.info(f"执行{direction_str}操作,金额: {size}")
|
||||
size = 25
|
||||
try:
|
||||
if marketPriceLongOrder == -1:
|
||||
self.click_safe('x://button[normalize-space(text()) ="市价"]')
|
||||
@@ -583,73 +403,12 @@ class BitmartOneFifthStrategy:
|
||||
curr = kline_data[-1]
|
||||
curr_time_str = datetime.datetime.fromtimestamp(curr['id']).strftime('%H:%M:%S')
|
||||
|
||||
# 优化:先获取持仓状态,再检测信号(传入持仓状态以便只关注反向信号)
|
||||
# 获取持仓状态
|
||||
if not self.get_position_status():
|
||||
logger.warning("获取仓位信息失败,使用缓存的持仓状态")
|
||||
|
||||
# ========== 第一分钟反手信号检测 ==========
|
||||
# 如果有持仓,先检测基于开仓价格的第一分钟反手信号
|
||||
first_min_direction = None
|
||||
first_min_trigger_price = None
|
||||
if self.start != 0:
|
||||
first_min_direction, first_min_trigger_price = self.check_first_minute_reverse_signal(curr, kline_data)
|
||||
|
||||
# 如果检测到第一分钟反手信号,优先执行
|
||||
if first_min_direction:
|
||||
curr_kline_id = curr['id']
|
||||
if self.last_trade_kline_id != curr_kline_id:
|
||||
logger.info(f"{'=' * 50}")
|
||||
# 安全获取开仓价格和前一根实体
|
||||
entry_price_display = self.entry_price if self.entry_price else (float(self.open_avg_price) if self.open_avg_price else 0)
|
||||
entry_body_display = self.entry_prev_body if self.entry_prev_body else 0
|
||||
logger.info(f"🔄 第一分钟反手信号触发!方向: {first_min_direction}, 触发价: {first_min_trigger_price:.2f}")
|
||||
logger.info(f" 开仓价: {entry_price_display:.2f}, 前一根实体/5: {entry_body_display/5:.2f}")
|
||||
logger.info(f" 当前持仓: {self.start} (1=多, -1=空)")
|
||||
|
||||
balance = self.get_available_balance()
|
||||
trade_size = (balance or 0) * self.risk_percent
|
||||
|
||||
if first_min_direction == 'long' and self.start == -1:
|
||||
logger.info("📈 第一分钟反手:平空仓,反手开多")
|
||||
self.平仓()
|
||||
time.sleep(1)
|
||||
self.开单(marketPriceLongOrder=1, size=trade_size)
|
||||
self.first_minute_reverse_executed = True
|
||||
self.last_trade_kline_id = curr_kline_id
|
||||
# 更新开仓信息
|
||||
valid_prev_idx, valid_prev = self.find_valid_prev_bar(kline_data, len(kline_data) - 1, self.min_body_size)
|
||||
if valid_prev:
|
||||
self.entry_prev_body = self.get_body_size(valid_prev)
|
||||
self.entry_price = float(curr['close'])
|
||||
self.entry_kline_id = curr_kline_id
|
||||
self.get_position_status()
|
||||
self._send_position_message(curr)
|
||||
|
||||
elif first_min_direction == 'short' and self.start == 1:
|
||||
logger.info("📉 第一分钟反手:平多仓,反手开空")
|
||||
self.平仓()
|
||||
time.sleep(1)
|
||||
self.开单(marketPriceLongOrder=-1, size=trade_size)
|
||||
self.first_minute_reverse_executed = True
|
||||
self.last_trade_kline_id = curr_kline_id
|
||||
# 更新开仓信息
|
||||
valid_prev_idx, valid_prev = self.find_valid_prev_bar(kline_data, len(kline_data) - 1, self.min_body_size)
|
||||
if valid_prev:
|
||||
self.entry_prev_body = self.get_body_size(valid_prev)
|
||||
self.entry_price = float(curr['close'])
|
||||
self.entry_kline_id = curr_kline_id
|
||||
self.get_position_status()
|
||||
self._send_position_message(curr)
|
||||
|
||||
logger.info(f"{'=' * 50}")
|
||||
time.sleep(self.check_interval)
|
||||
continue
|
||||
|
||||
# ========== 原有的五分之一信号检测 ==========
|
||||
# 传入当前持仓状态,确保有持仓时只关注反向信号
|
||||
direction, trigger_price, valid_prev, curr_kline = self.check_realtime_trigger(
|
||||
kline_data, current_position=self.start
|
||||
)
|
||||
# 检测五分之一策略信号(与回测逻辑完全一致)
|
||||
direction, trigger_price, valid_prev, curr_kline = self.check_realtime_trigger(kline_data)
|
||||
|
||||
if direction:
|
||||
curr_kline_id = curr_kline['id']
|
||||
@@ -663,15 +422,6 @@ class BitmartOneFifthStrategy:
|
||||
prev_type = "阳线" if self.is_bullish(valid_prev) else "阴线"
|
||||
prev_body = self.get_body_size(valid_prev)
|
||||
|
||||
# 由于 check_realtime_trigger 已经考虑了持仓状态,这里理论上不会出现同向信号
|
||||
# 但保留这个检查作为安全措施
|
||||
if (direction == "long" and self.start == 1) or (direction == "short" and self.start == -1):
|
||||
logger.debug(f"同向信号被过滤: direction={direction}, position={self.start}")
|
||||
self.last_trigger_kline_id = curr_kline_id
|
||||
self.last_trigger_direction = direction
|
||||
time.sleep(self.check_interval)
|
||||
continue
|
||||
|
||||
logger.info(f"{'=' * 50}")
|
||||
logger.info(f"🚨 检测到{direction}信号!触发价格: {trigger_price:.2f}")
|
||||
logger.info(
|
||||
@@ -684,40 +434,44 @@ class BitmartOneFifthStrategy:
|
||||
trade_size = (balance or 0) * self.risk_percent
|
||||
executed = False
|
||||
|
||||
# 执行交易逻辑(与回测一致)
|
||||
if direction == "long":
|
||||
if self.start == -1:
|
||||
# 持空仓遇到做多信号 -> 平空并反手开多
|
||||
logger.info("📈 平空仓,反手开多")
|
||||
self.平仓()
|
||||
time.sleep(1)
|
||||
self.开单(marketPriceLongOrder=1, size=trade_size)
|
||||
executed = True
|
||||
elif self.start == 0:
|
||||
# 无仓位遇到做多信号 -> 开多
|
||||
logger.info("📈 无仓位,开多")
|
||||
self.开单(marketPriceLongOrder=1, size=trade_size)
|
||||
executed = True
|
||||
elif self.start == 1:
|
||||
# 持多仓遇到做多信号 -> 不操作
|
||||
logger.debug("已持有多仓,忽略做多信号")
|
||||
elif direction == "short":
|
||||
if self.start == 1:
|
||||
# 持多仓遇到做空信号 -> 平多并反手开空
|
||||
logger.info("📉 平多仓,反手开空")
|
||||
self.平仓()
|
||||
time.sleep(1)
|
||||
self.开单(marketPriceLongOrder=-1, size=trade_size)
|
||||
executed = True
|
||||
elif self.start == 0:
|
||||
# 无仓位遇到做空信号 -> 开空
|
||||
logger.info("📉 无仓位,开空")
|
||||
self.开单(marketPriceLongOrder=-1, size=trade_size)
|
||||
executed = True
|
||||
elif self.start == -1:
|
||||
# 持空仓遇到做空信号 -> 不操作
|
||||
logger.debug("已持有空仓,忽略做空信号")
|
||||
|
||||
self.last_trigger_kline_id = curr_kline_id
|
||||
self.last_trigger_direction = direction
|
||||
if executed:
|
||||
self.last_trade_kline_id = curr_kline_id
|
||||
# 记录开仓信息,用于第一分钟反手信号检测
|
||||
self.entry_price = trigger_price # 开仓触发价格
|
||||
self.entry_prev_body = self.get_body_size(valid_prev) # 前一根K线实体大小
|
||||
self.entry_kline_id = curr_kline_id # 开仓时的K线ID
|
||||
self.first_minute_reverse_executed = False # 重置第一分钟反手标记
|
||||
logger.info(f" 记录开仓信息:开仓价={self.entry_price:.2f}, 前一根实体={self.entry_prev_body:.2f}, K线ID={self.entry_kline_id}")
|
||||
|
||||
self.get_position_status()
|
||||
self._send_position_message(curr_kline)
|
||||
last_report_time = time.time()
|
||||
|
||||
641
交易/weex-三分之一策略-5分钟交易.py
Normal file
641
交易/weex-三分之一策略-5分钟交易.py
Normal file
@@ -0,0 +1,641 @@
|
||||
"""
|
||||
WEEX ETH-USDT 永续合约 — 三分之一策略(5分钟K线)
|
||||
|
||||
策略规则(与 bitmart/三分之一策略-5分钟交易.py 一致):
|
||||
1. 触发价格(前一根有效K线实体>=0.1):
|
||||
- 做多触发价 = 收盘价 + 实体/3,做空触发价 = 收盘价 - 实体/3
|
||||
2. 信号:当前5分钟K线最高>=做多触发→多;最低<=做空触发→空;同根多空都触发用开盘价距离判断先后
|
||||
3. 反手一:持空且当前涨到上根最高且上根上影线>0.01%→反手多;持多且当前跌到上根最低且上根下影线>0.01%→反手空
|
||||
4. 反手二:持多且上根上影线>0.01%且当前跌到上根开盘→反手空;持空且上根下影线>0.01%且当前涨到上根开盘→反手多
|
||||
5. 同一根5分钟K线内只交易一次
|
||||
"""
|
||||
import random
|
||||
import time
|
||||
import datetime
|
||||
from typing import Optional, Dict, List, Tuple
|
||||
from tqdm import tqdm
|
||||
from loguru import logger
|
||||
from DrissionPage import ChromiumOptions, ChromiumPage
|
||||
from curl_cffi import requests
|
||||
from bit_tools import openBrowser
|
||||
from 交易.tools import send_dingtalk_message
|
||||
|
||||
|
||||
# ==================== 配置常量 ====================
|
||||
class Config:
|
||||
TGE_URL = "http://127.0.0.1:50326"
|
||||
TGE_AUTHORIZATION = "Bearer asp_174003986c9b0799677c5b2c1adb76e402735d753bc91a91"
|
||||
|
||||
CONTRACT_ID = "10000002"
|
||||
PRODUCT_CODE = "cmt_ethusdt"
|
||||
KLINE_TYPE = "MINUTE_5" # 5分钟K线(三分之一策略)
|
||||
KLINE_LIMIT = 100
|
||||
|
||||
TRADING_URL = "https://www.weex.com/zh-CN/futures/ETH-USDT"
|
||||
POSITION_RATIO = 100
|
||||
|
||||
MAX_RETRY_ATTEMPTS = 3
|
||||
RETRY_DELAY = 1
|
||||
|
||||
# 三分之一策略参数
|
||||
MIN_BODY_SIZE = 0.1
|
||||
MIN_SHADOW_PCT = 0.01
|
||||
|
||||
|
||||
# ==================== 三分之一策略分析器 ====================
|
||||
class OneThirdStrategyAnalyzer:
|
||||
"""三分之一策略 K 线分析(5分钟):触发价=收盘价±实体/3 + 两个反手信号"""
|
||||
|
||||
@staticmethod
|
||||
def get_body_size(candle: Dict) -> float:
|
||||
return abs(float(candle['open']) - float(candle['close']))
|
||||
|
||||
@staticmethod
|
||||
def find_valid_prev_bar(all_data: List[Dict], current_idx: int, min_body_size: float = 0.1) -> Tuple[
|
||||
Optional[int], Optional[Dict]]:
|
||||
if current_idx <= 0:
|
||||
return None, None
|
||||
for i in range(current_idx - 1, -1, -1):
|
||||
prev = all_data[i]
|
||||
if OneThirdStrategyAnalyzer.get_body_size(prev) >= min_body_size:
|
||||
return i, prev
|
||||
return None, None
|
||||
|
||||
@staticmethod
|
||||
def get_one_third_levels(prev: Dict) -> Tuple[Optional[float], Optional[float]]:
|
||||
p_open, p_close = float(prev['open']), float(prev['close'])
|
||||
body = abs(p_open - p_close)
|
||||
if body < 0.001:
|
||||
return None, None
|
||||
return p_close + body / 3, p_close - body / 3
|
||||
|
||||
@staticmethod
|
||||
def get_upper_shadow(candle: Dict) -> float:
|
||||
o, c, h = float(candle['open']), float(candle['close']), float(candle['high'])
|
||||
return h - max(o, c)
|
||||
|
||||
@staticmethod
|
||||
def get_lower_shadow(candle: Dict) -> float:
|
||||
o, c, l = float(candle['open']), float(candle['close']), float(candle['low'])
|
||||
return min(o, c) - l
|
||||
|
||||
@staticmethod
|
||||
def upper_shadow_pct(candle: Dict) -> float:
|
||||
o = float(candle['open'])
|
||||
return (OneThirdStrategyAnalyzer.get_upper_shadow(candle) / o * 100) if o > 0 else 0.0
|
||||
|
||||
@staticmethod
|
||||
def lower_shadow_pct(candle: Dict) -> float:
|
||||
o = float(candle['open'])
|
||||
return (OneThirdStrategyAnalyzer.get_lower_shadow(candle) / o * 100) if o > 0 else 0.0
|
||||
|
||||
@staticmethod
|
||||
def check_reverse_by_prev_high_low(kline_data: List[Dict], start: int) -> Tuple[Optional[str], Optional[Dict]]:
|
||||
if len(kline_data) < 2:
|
||||
return None, None
|
||||
curr, prev = kline_data[-1], kline_data[-2]
|
||||
curr_high = float(curr['high'])
|
||||
curr_low = float(curr['low'])
|
||||
prev_high = float(prev['high'])
|
||||
prev_low = float(prev['low'])
|
||||
if start == -1 and curr_high >= prev_high and OneThirdStrategyAnalyzer.upper_shadow_pct(
|
||||
prev) > Config.MIN_SHADOW_PCT:
|
||||
return 'long', prev
|
||||
if start == 1 and curr_low <= prev_low and OneThirdStrategyAnalyzer.lower_shadow_pct(
|
||||
prev) > Config.MIN_SHADOW_PCT:
|
||||
return 'short', prev
|
||||
return None, None
|
||||
|
||||
@staticmethod
|
||||
def check_reverse_by_prev_open(kline_data: List[Dict], start: int) -> Tuple[Optional[str], Optional[Dict]]:
|
||||
if len(kline_data) < 2:
|
||||
return None, None
|
||||
curr, prev = kline_data[-1], kline_data[-2]
|
||||
curr_high = float(curr['high'])
|
||||
curr_low = float(curr['low'])
|
||||
prev_open = float(prev['open'])
|
||||
if start == 1 and OneThirdStrategyAnalyzer.upper_shadow_pct(
|
||||
prev) > Config.MIN_SHADOW_PCT and curr_low <= prev_open:
|
||||
return 'short', prev
|
||||
if start == -1 and OneThirdStrategyAnalyzer.lower_shadow_pct(
|
||||
prev) > Config.MIN_SHADOW_PCT and curr_high >= prev_open:
|
||||
return 'long', prev
|
||||
return None, None
|
||||
|
||||
@staticmethod
|
||||
def check_realtime_trigger(
|
||||
kline_data: List[Dict],
|
||||
last_trigger_kline_id: Optional[int],
|
||||
last_trigger_direction: Optional[str],
|
||||
) -> Tuple[Optional[str], Optional[float], Optional[Dict], Optional[Dict]]:
|
||||
if len(kline_data) < 2:
|
||||
return None, None, None, None
|
||||
curr = kline_data[-1]
|
||||
curr_kline_id = curr['id']
|
||||
curr_high = float(curr['high'])
|
||||
curr_low = float(curr['low'])
|
||||
curr_open = float(curr['open'])
|
||||
|
||||
valid_prev_idx, prev = OneThirdStrategyAnalyzer.find_valid_prev_bar(
|
||||
kline_data, len(kline_data) - 1, Config.MIN_BODY_SIZE
|
||||
)
|
||||
if prev is None:
|
||||
return None, None, None, None
|
||||
|
||||
long_trigger, short_trigger = OneThirdStrategyAnalyzer.get_one_third_levels(prev)
|
||||
if long_trigger is None:
|
||||
return None, None, None, None
|
||||
|
||||
long_triggered = curr_high >= long_trigger
|
||||
short_triggered = curr_low <= short_trigger
|
||||
both_triggered = long_triggered and short_triggered
|
||||
|
||||
direction = None
|
||||
trigger_price = None
|
||||
if both_triggered:
|
||||
dist_to_long = abs(long_trigger - curr_open)
|
||||
dist_to_short = abs(short_trigger - curr_open)
|
||||
if dist_to_short <= dist_to_long:
|
||||
direction, trigger_price = 'short', short_trigger
|
||||
else:
|
||||
direction, trigger_price = 'long', long_trigger
|
||||
elif short_triggered:
|
||||
direction, trigger_price = 'short', short_trigger
|
||||
elif long_triggered:
|
||||
direction, trigger_price = 'long', long_trigger
|
||||
|
||||
if direction is None:
|
||||
return None, None, None, None
|
||||
if last_trigger_kline_id == curr_kline_id and last_trigger_direction == direction:
|
||||
return None, None, None, None
|
||||
return direction, trigger_price, prev, curr
|
||||
|
||||
|
||||
# ==================== 浏览器管理器 ====================
|
||||
class BrowserManager:
|
||||
def __init__(self, tge_id, tge_url: str, tge_headers: Dict):
|
||||
self.tge_id = tge_id
|
||||
self.tge_url = tge_url
|
||||
self.tge_headers = tge_headers
|
||||
self.tge_port: Optional[int] = None
|
||||
self.page: Optional[ChromiumPage] = None
|
||||
|
||||
def openBrowser(self):
|
||||
try:
|
||||
bit_port = openBrowser(id=self.tge_id)
|
||||
co = ChromiumOptions()
|
||||
co.set_local_port(port=bit_port)
|
||||
self.page = ChromiumPage(addr_or_opts=co)
|
||||
self.tge_port = bit_port
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def take_over_browser(self) -> bool:
|
||||
if not self.tge_port:
|
||||
return False
|
||||
try:
|
||||
co = ChromiumOptions()
|
||||
co.set_local_port(self.tge_port)
|
||||
self.page = ChromiumPage(addr_or_opts=co)
|
||||
self.page.set.window.max()
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def close_extra_tabs(self) -> bool:
|
||||
if not self.page:
|
||||
return False
|
||||
try:
|
||||
for idx, tab in enumerate(self.page.get_tabs()):
|
||||
if idx > 0:
|
||||
tab.close()
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
# ==================== WEEX API 客户端 ====================
|
||||
class WEEXApiClient:
|
||||
def __init__(self):
|
||||
self.session = requests.Session()
|
||||
self.headers: Optional[Dict] = None
|
||||
|
||||
def update_headers(self, headers: Dict) -> None:
|
||||
if not self.headers:
|
||||
self.session.headers = headers
|
||||
else:
|
||||
self.session.headers.update(headers)
|
||||
self.headers = headers
|
||||
|
||||
def get_kline_data(
|
||||
self,
|
||||
contract_id: str = Config.CONTRACT_ID,
|
||||
product_code: str = Config.PRODUCT_CODE,
|
||||
kline_type: str = Config.KLINE_TYPE,
|
||||
limit: int = Config.KLINE_LIMIT,
|
||||
) -> List[Dict]:
|
||||
params = {
|
||||
'contractId': contract_id,
|
||||
'productCode': product_code,
|
||||
'priceType': 'LAST_PRICE',
|
||||
'klineType': kline_type,
|
||||
'limit': str(limit),
|
||||
'timeZone': 'string',
|
||||
'languageType': '1',
|
||||
'sign': 'SIGN',
|
||||
}
|
||||
for attempt in range(Config.MAX_RETRY_ATTEMPTS):
|
||||
try:
|
||||
response = self.session.get(
|
||||
'https://http-gateway2.elconvo.com/api/v1/public/quote/v1/getKlineV2',
|
||||
params=params,
|
||||
timeout=15,
|
||||
)
|
||||
if response.status_code != 200:
|
||||
if attempt < Config.MAX_RETRY_ATTEMPTS - 1:
|
||||
time.sleep(Config.RETRY_DELAY)
|
||||
continue
|
||||
response_data = response.json()
|
||||
if "data" not in response_data or "dataList" not in response_data["data"]:
|
||||
continue
|
||||
result = response_data["data"]["dataList"]
|
||||
kline_data = []
|
||||
for item in result:
|
||||
kline_data.append({
|
||||
'id': int(item[4]),
|
||||
'open': float(item[3]),
|
||||
'high': float(item[1]),
|
||||
'low': float(item[2]),
|
||||
'close': float(item[0]),
|
||||
})
|
||||
return kline_data
|
||||
except Exception as e:
|
||||
if attempt < Config.MAX_RETRY_ATTEMPTS - 1:
|
||||
time.sleep(Config.RETRY_DELAY)
|
||||
return []
|
||||
|
||||
def get_available_balance(self) -> Optional[float]:
|
||||
for attempt in range(Config.MAX_RETRY_ATTEMPTS):
|
||||
try:
|
||||
response = self.session.post(
|
||||
'https://gateway2.ngsvsfx.cn/v1/gw/assetsWithBalance/new',
|
||||
timeout=15,
|
||||
)
|
||||
return float(response.json()["data"]["newContract"]["balanceList"][0]["available"])
|
||||
except Exception:
|
||||
if attempt < Config.MAX_RETRY_ATTEMPTS - 1:
|
||||
time.sleep(Config.RETRY_DELAY)
|
||||
return None
|
||||
|
||||
def get_position_status(self) -> Tuple[bool, Optional[List]]:
|
||||
json_data = {
|
||||
'filterContractIdList': [10000002],
|
||||
'limit': 100,
|
||||
'languageType': 0,
|
||||
'sign': 'SIGN',
|
||||
'timeZone': 'string',
|
||||
}
|
||||
for attempt in range(Config.MAX_RETRY_ATTEMPTS):
|
||||
try:
|
||||
response = self.session.post(
|
||||
'https://http-gateway2.janapw.com/api/v1/private/order/v2/getHistoryOrderFillTransactionPage',
|
||||
json=json_data,
|
||||
timeout=15,
|
||||
)
|
||||
datas = response.json()["data"]["dataList"]
|
||||
if not datas:
|
||||
return True, None
|
||||
return True, datas
|
||||
except Exception:
|
||||
if attempt < Config.MAX_RETRY_ATTEMPTS - 1:
|
||||
time.sleep(Config.RETRY_DELAY)
|
||||
return False, None
|
||||
|
||||
|
||||
# ==================== Token 管理器 ====================
|
||||
class TokenManager:
|
||||
def __init__(self, api_client: WEEXApiClient, page: ChromiumPage):
|
||||
self.api_client = api_client
|
||||
self.page = page
|
||||
|
||||
def get_token(self) -> bool:
|
||||
tab = self.page.new_tab()
|
||||
tab.listen.start("/user/security/getLanguageType")
|
||||
try:
|
||||
for attempt in range(Config.MAX_RETRY_ATTEMPTS):
|
||||
try:
|
||||
tab.get(url=Config.TRADING_URL)
|
||||
res = tab.listen.wait(timeout=5)
|
||||
if res.request.headers.get("U-TOKEN"):
|
||||
self.api_client.update_headers(res.request.headers)
|
||||
return True
|
||||
except Exception:
|
||||
if attempt < Config.MAX_RETRY_ATTEMPTS - 1:
|
||||
time.sleep(Config.RETRY_DELAY)
|
||||
return False
|
||||
finally:
|
||||
tab.close()
|
||||
|
||||
|
||||
# ==================== 交易执行器 ====================
|
||||
class TradingExecutor:
|
||||
def __init__(self, page: ChromiumPage, api_client: WEEXApiClient):
|
||||
self.page = page
|
||||
self.api_client = api_client
|
||||
|
||||
def navigate_to_trading_page(self) -> bool:
|
||||
try:
|
||||
self.page.ele('x:(//button[normalize-space(text()) = "市价"])').click()
|
||||
time.sleep(1)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def close_all_positions(self) -> bool:
|
||||
try:
|
||||
self.page.ele('x:(//span[normalize-space(text()) = "闪电平仓"])').scroll.to_see(center=True)
|
||||
time.sleep(1)
|
||||
self.page.ele('x:(//span[normalize-space(text()) = "闪电平仓"])').click(by_js=True)
|
||||
time.sleep(3)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def open_long(self, amount: float) -> bool:
|
||||
try:
|
||||
self.page.ele('x://input[@placeholder="请输入数量"]').input(amount)
|
||||
time.sleep(1)
|
||||
self.page.ele('x://*[normalize-space(text()) ="买入开多"]').click(by_js=True)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def open_short(self, amount: float) -> bool:
|
||||
try:
|
||||
self.page.ele('x://input[@placeholder="请输入数量"]').input(amount)
|
||||
time.sleep(1)
|
||||
self.page.ele('x://*[normalize-space(text()) ="卖出开空"]').click(by_js=True)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def execute_trade(self, direction: str, current_position: int, amount: float) -> bool:
|
||||
if (direction == "long" and current_position == PositionManager.POSITION_LONG) or \
|
||||
(direction == "short" and current_position == PositionManager.POSITION_SHORT):
|
||||
return True
|
||||
if not self.navigate_to_trading_page():
|
||||
return False
|
||||
try:
|
||||
if direction == "long":
|
||||
if current_position == 0:
|
||||
return self.open_long(amount)
|
||||
elif current_position == -1:
|
||||
if self.close_all_positions():
|
||||
time.sleep(1)
|
||||
return self.open_long(amount)
|
||||
elif direction == "short":
|
||||
if current_position == 0:
|
||||
return self.open_short(amount)
|
||||
elif current_position == 1:
|
||||
if self.close_all_positions():
|
||||
time.sleep(1)
|
||||
return self.open_short(amount)
|
||||
except Exception:
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
# ==================== 持仓管理器 ====================
|
||||
class PositionManager:
|
||||
POSITION_SHORT = -1
|
||||
POSITION_NONE = 0
|
||||
POSITION_LONG = 1
|
||||
|
||||
def __init__(self, trading_executor: TradingExecutor):
|
||||
self.trading_executor = trading_executor
|
||||
self.current_position: int = self.POSITION_NONE
|
||||
self.position_data: Optional[List] = None
|
||||
|
||||
def update_position(self, position_data: Optional[List]) -> None:
|
||||
self.position_data = position_data
|
||||
if not position_data:
|
||||
self.current_position = self.POSITION_NONE
|
||||
return
|
||||
position_data = list(position_data)
|
||||
position_data.reverse()
|
||||
start, start1 = 0, 0
|
||||
for i in position_data:
|
||||
direction = i.get("legacyOrderDirection")
|
||||
if direction == "CLOSE_SHORT":
|
||||
start = 0
|
||||
elif direction == "CLOSE_LONG":
|
||||
start1 = 0
|
||||
elif direction == "OPEN_SHORT":
|
||||
start -= 1
|
||||
elif direction == "OPEN_LONG":
|
||||
start1 += 1
|
||||
if start1:
|
||||
self.current_position = self.POSITION_LONG
|
||||
elif start:
|
||||
self.current_position = self.POSITION_SHORT
|
||||
else:
|
||||
self.current_position = self.POSITION_NONE
|
||||
|
||||
|
||||
# ==================== 消息发送 ====================
|
||||
class MessageSender:
|
||||
@staticmethod
|
||||
def send(msg: str, is_error: bool = False) -> None:
|
||||
prefix = "❌weex三分之一:" if is_error else "🔔weex三分之一:"
|
||||
for _ in range(15 if is_error else 1):
|
||||
send_dingtalk_message(f"{prefix}{msg}")
|
||||
|
||||
|
||||
# ==================== 5分钟K线时间工具 ====================
|
||||
class TimeUtils5m:
|
||||
@staticmethod
|
||||
def get_current_kline_timestamp() -> int:
|
||||
"""当前所在 5 分钟 K 线的时间戳(毫秒)"""
|
||||
t = time.time()
|
||||
dt = datetime.datetime.fromtimestamp(t)
|
||||
minute = (dt.minute // 5) * 5
|
||||
target = dt.replace(minute=minute, second=0, microsecond=0)
|
||||
return int(target.timestamp()) * 1000
|
||||
|
||||
@staticmethod
|
||||
def get_progress_bar_value() -> int:
|
||||
"""0~4 表示当前 5 分钟内的分钟数"""
|
||||
return datetime.datetime.now().minute % 5
|
||||
|
||||
|
||||
# ==================== 主交易类 ====================
|
||||
class WeexOneThirdTransaction:
|
||||
"""WEEX 三分之一策略(5分钟K线)自动交易"""
|
||||
|
||||
def __init__(self, tge_id):
|
||||
self.tge_id = tge_id
|
||||
self.tge_headers = {
|
||||
"Authorization": Config.TGE_AUTHORIZATION,
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
self.browser_manager = BrowserManager(tge_id, Config.TGE_URL, self.tge_headers)
|
||||
self.api_client = WEEXApiClient()
|
||||
self.position_manager: Optional[PositionManager] = None
|
||||
self.trading_executor: Optional[TradingExecutor] = None
|
||||
self.token_manager: Optional[TokenManager] = None
|
||||
|
||||
self.pbar: Optional[tqdm] = None
|
||||
self.last_kline_timestamp: Optional[int] = None
|
||||
self.kline_data: List[Dict] = []
|
||||
self.last_trigger_kline_id: Optional[int] = None
|
||||
self.last_trigger_direction: Optional[str] = None
|
||||
self.last_trade_kline_id: Optional[int] = None
|
||||
|
||||
def initialize(self) -> bool:
|
||||
for i in range(3):
|
||||
if self.browser_manager.openBrowser():
|
||||
break
|
||||
else:
|
||||
MessageSender.send("打开浏览器失败", is_error=True)
|
||||
|
||||
if not self.browser_manager.take_over_browser():
|
||||
MessageSender.send("接管浏览器失败", is_error=True)
|
||||
return False
|
||||
self.browser_manager.close_extra_tabs()
|
||||
page = self.browser_manager.page
|
||||
self.trading_executor = TradingExecutor(page, self.api_client)
|
||||
self.position_manager = PositionManager(self.trading_executor)
|
||||
self.token_manager = TokenManager(self.api_client, page)
|
||||
page.get(url=Config.TRADING_URL)
|
||||
if not self.token_manager.get_token():
|
||||
logger.warning("初始化获取 token 失败,将在获取K线时重试")
|
||||
self.pbar = tqdm(total=5, desc="等待5分钟K线", ncols=80)
|
||||
return True
|
||||
|
||||
def fetch_and_update_kline(self) -> bool:
|
||||
if not self.token_manager.get_token():
|
||||
return False
|
||||
kline_data = self.api_client.get_kline_data()
|
||||
if not kline_data or len(kline_data) < 3:
|
||||
return False
|
||||
sorted_data = sorted(kline_data, key=lambda x: x["id"])
|
||||
self.kline_data = sorted_data
|
||||
current_kline_id = sorted_data[-1]["id"]
|
||||
current_ts = TimeUtils5m.get_current_kline_timestamp()
|
||||
if current_kline_id != current_ts:
|
||||
return False
|
||||
if self.last_kline_timestamp == current_ts:
|
||||
return False
|
||||
self.last_kline_timestamp = current_ts
|
||||
return True
|
||||
|
||||
def sync_position_status(self) -> bool:
|
||||
success, position_data = self.api_client.get_position_status()
|
||||
if not success:
|
||||
return False
|
||||
self.position_manager.update_position(position_data)
|
||||
return True
|
||||
|
||||
def process_trading_logic(self) -> None:
|
||||
self.token_manager.get_token()
|
||||
self.browser_manager.page.get(url=Config.TRADING_URL)
|
||||
if not self.sync_position_status():
|
||||
return
|
||||
|
||||
kline_data = self.kline_data
|
||||
curr = kline_data[-1]
|
||||
curr_kline_id = curr["id"]
|
||||
start = self.position_manager.current_position
|
||||
|
||||
# 反手一:涨到上根最高/跌到上根最低 + 影线>0.01%
|
||||
rev_dir, rev_prev = OneThirdStrategyAnalyzer.check_reverse_by_prev_high_low(kline_data, start)
|
||||
rev_type = "一"
|
||||
if not rev_dir:
|
||||
rev_dir, rev_prev = OneThirdStrategyAnalyzer.check_reverse_by_prev_open(kline_data, start)
|
||||
rev_type = "二"
|
||||
|
||||
if rev_dir and self.last_trade_kline_id != curr_kline_id:
|
||||
balance = self.api_client.get_available_balance()
|
||||
amount = int((balance or 0) / Config.POSITION_RATIO)
|
||||
if self.trading_executor.execute_trade(rev_dir, start, amount):
|
||||
self.last_trade_kline_id = curr_kline_id
|
||||
self.last_trigger_kline_id = curr_kline_id
|
||||
self.last_trigger_direction = rev_dir
|
||||
if rev_dir == "long":
|
||||
self.position_manager.current_position = PositionManager.POSITION_LONG
|
||||
else:
|
||||
self.position_manager.current_position = PositionManager.POSITION_SHORT
|
||||
MessageSender.send(f"反手信号{rev_type} {rev_dir},金额 {amount}")
|
||||
return
|
||||
|
||||
# 主信号:三分之一触发
|
||||
direction, trigger_price, valid_prev, curr_kline = OneThirdStrategyAnalyzer.check_realtime_trigger(
|
||||
kline_data, self.last_trigger_kline_id, self.last_trigger_direction
|
||||
)
|
||||
if not direction:
|
||||
return
|
||||
|
||||
if self.last_trade_kline_id == curr_kline_id:
|
||||
self.last_trigger_kline_id = curr_kline_id
|
||||
self.last_trigger_direction = direction
|
||||
return
|
||||
|
||||
if (direction == "long" and start == PositionManager.POSITION_LONG) or \
|
||||
(direction == "short" and start == PositionManager.POSITION_SHORT):
|
||||
self.last_trigger_kline_id = curr_kline_id
|
||||
self.last_trigger_direction = direction
|
||||
return
|
||||
|
||||
balance = self.api_client.get_available_balance()
|
||||
if balance is None:
|
||||
return
|
||||
amount = int(balance / Config.POSITION_RATIO)
|
||||
if self.trading_executor.execute_trade(direction, start, amount):
|
||||
self.last_trade_kline_id = curr_kline_id
|
||||
self.last_trigger_kline_id = curr_kline_id
|
||||
self.last_trigger_direction = direction
|
||||
if direction == "long":
|
||||
self.position_manager.current_position = PositionManager.POSITION_LONG
|
||||
else:
|
||||
self.position_manager.current_position = PositionManager.POSITION_SHORT
|
||||
MessageSender.send(f"三分之一信号 {direction} 触发价={trigger_price:.2f} 金额={amount}")
|
||||
|
||||
def run(self) -> None:
|
||||
|
||||
logger.info("WEEX 三分之一策略(5分钟K线)开始运行")
|
||||
while True:
|
||||
if not self.initialize():
|
||||
return
|
||||
try:
|
||||
if self.pbar:
|
||||
self.pbar.n = TimeUtils5m.get_progress_bar_value()
|
||||
self.pbar.refresh()
|
||||
current_ts = TimeUtils5m.get_current_kline_timestamp()
|
||||
if self.last_kline_timestamp == current_ts:
|
||||
time.sleep(10)
|
||||
continue
|
||||
if not self.fetch_and_update_kline():
|
||||
time.sleep(10)
|
||||
continue
|
||||
self.process_trading_logic()
|
||||
if self.pbar:
|
||||
self.pbar.reset()
|
||||
time.sleep(5)
|
||||
except KeyboardInterrupt:
|
||||
break
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
MessageSender.send(f"运行出错: {e}", is_error=True)
|
||||
time.sleep(10)
|
||||
|
||||
if random.randint(1, 10) > 7:
|
||||
self.browser_manager.page.close()
|
||||
time.sleep(5)
|
||||
|
||||
def action(self) -> None:
|
||||
self.run()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
WeexOneThirdTransaction(tge_id="86837a981aba4576be6916a0ef6ad785").action()
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -1,277 +0,0 @@
|
||||
开仓时间,平仓时间,数量,开仓价,平仓价,盈亏,类型,开仓订单ID,平仓订单ID,时间_x
|
||||
2025-08-28 09:35:18+08:00,2025-08-28 09:44:18+08:00,664.0,4514.69,4507.43,4820.639999999541,平空,e1d3a2da-219e-436f-ac9e-a7ea93de15bd,abfa0508-9399-4f1b-90c7-7716c3e7f511,2025-08-28 01:44:00
|
||||
2025-08-28 10:06:42+08:00,2025-08-28 10:10:07+08:00,1106.0,4519.88,4515.64,-4689.439999999759,平多,594bd10c-2120-4692-a8eb-9f5325e26ef0,bd643cab-14d4-414d-912e-e9257ddb96de,2025-08-28 02:10:00
|
||||
2025-08-28 10:45:07+08:00,2025-08-28 10:51:41+08:00,1108.0,4512.13,4516.84,-5218.68000000004,平空,fc0c73ab-9fff-41b5-98c1-ef6f7d300256,206688da-848a-4822-9571-c05302ab7a29,2025-08-28 02:51:00
|
||||
2025-08-28 11:07:30+08:00,2025-08-28 11:13:19+08:00,200.0,4525.0,4529.25,850.0,平多,6197fb5c-6202-422f-9012-5395c5393fb3,59d8859f-2b64-4e8b-aefe-57998b198841,2025-08-28 03:13:00
|
||||
2025-08-28 11:07:30+08:00,2025-08-28 11:13:19+08:00,100.0,4525.0,4529.25,425.0,平多,794c0925-6e48-4b9a-a162-d31d7a3c42cc,59d8859f-2b64-4e8b-aefe-57998b198841,2025-08-28 03:13:00
|
||||
2025-08-28 11:07:31+08:00,2025-08-28 11:13:19+08:00,483.0,4525.0,4529.25,2052.75,平多,65f7d764-ad05-40c7-bce1-6b227ad5f5c0,59d8859f-2b64-4e8b-aefe-57998b198841,2025-08-28 03:13:00
|
||||
2025-08-28 11:07:31+08:00,2025-08-28 11:13:19+08:00,100.0,4525.0,4529.25,425.0,平多,35e1c160-4129-425a-ae46-b8fb45d53ec8,59d8859f-2b64-4e8b-aefe-57998b198841,2025-08-28 03:13:00
|
||||
2025-08-28 11:28:58+08:00,2025-08-28 11:34:21+08:00,400.0,4533.74,4537.34,1440.0000000001455,平多,a983b8f5-70d1-441b-87df-397c6c1ce843,3e161e4e-f367-421a-bc0d-8095f7b79675,2025-08-28 03:34:00
|
||||
2025-08-28 11:28:58+08:00,2025-08-28 11:34:21+08:00,482.0,4533.74,4537.34,1735.2000000001754,平多,a983b8f5-70d1-441b-87df-397c6c1ce843,45ddb145-d637-4985-9c4b-3dee2fd189b5,2025-08-28 03:34:00
|
||||
2025-08-28 12:22:53+08:00,2025-08-28 12:23:33+08:00,478.0,4550.8,4545.04,-2753.2800000001043,平多,37d97b24-c1fd-4a2d-ba0e-75b818062686,fcfe39c7-0bcf-47f6-9b48-a1dd70bd5444,2025-08-28 04:23:00
|
||||
2025-08-28 12:22:53+08:00,2025-08-28 12:23:33+08:00,400.0,4550.8,4545.04,-2304.0000000000873,平多,37d97b24-c1fd-4a2d-ba0e-75b818062686,867b1b2c-0fe5-466b-a89f-ec8dce25bcda,2025-08-28 04:23:00
|
||||
2025-08-28 12:26:39+08:00,2025-08-28 12:34:19+08:00,879.0,4549.6,4555.44,5133.3599999993285,平多,0a3a08a1-61c7-4997-b13c-4da05b2c5dbc,ad149e7f-7088-4dd9-9698-9b7bd615478e,2025-08-28 04:34:00
|
||||
2025-08-28 12:46:19+08:00,2025-08-28 12:48:10+08:00,600.0,4561.8,4567.53,-3437.999999999738,平空,18929fa1-2cc7-4e7f-8e76-c3937f16f877,8283a8b1-17f1-4818-843d-33877996af35,2025-08-28 04:48:00
|
||||
2025-08-28 12:46:19+08:00,2025-08-28 12:48:10+08:00,276.0,4561.8,4567.53,-1581.4799999998795,平空,18929fa1-2cc7-4e7f-8e76-c3937f16f877,c0a1d021-0708-476e-a766-722bc73ab740,2025-08-28 04:48:00
|
||||
2025-08-28 12:53:40+08:00,2025-08-28 12:57:03+08:00,873.0,4577.05,4569.51,6582.419999999968,平空,16f8970d-ef44-4026-ba19-9c9ff6dc70e1,e1f9de7d-115e-4289-8acd-bbba16e04ff0,2025-08-28 04:57:00
|
||||
2025-08-28 12:58:03+08:00,2025-08-28 13:01:30+08:00,475.0,4567.45,4572.58,-2436.750000000052,平空,8dbfcfc8-466c-4111-acdf-316a2a8c7428,3e0a22b4-7707-4332-b80d-87d3b07cf3cc,2025-08-28 05:01:00
|
||||
2025-08-28 12:58:03+08:00,2025-08-28 13:01:30+08:00,400.0,4567.45,4572.58,-2052.0000000000437,平空,8dbfcfc8-466c-4111-acdf-316a2a8c7428,29775823-c3fc-4dbc-8afa-f9587f803f2a,2025-08-28 05:01:00
|
||||
2025-08-28 13:32:49+08:00,2025-08-28 13:41:18+08:00,721.0,4564.18,4565.88,1225.6999999998689,平多,6c0a64d8-b890-42bc-ae09-152919931d57,5bba77bd-b49f-4c6d-9a12-fe062c687cae,2025-08-28 05:41:00
|
||||
2025-08-28 13:32:49+08:00,2025-08-28 13:41:18+08:00,155.0,4564.18,4565.88,263.4999999999718,平多,c0e17c5e-6a39-465c-a3b3-347f1af3d772,5bba77bd-b49f-4c6d-9a12-fe062c687cae,2025-08-28 05:41:00
|
||||
2025-08-28 13:52:44+08:00,2025-08-28 13:57:25+08:00,400.0,4555.2,4559.95,-1900.0,平空,8f641745-890a-4b9c-8d83-10163234429d,dd5f8c6a-74da-4984-aa24-eebfbceb6d22,2025-08-28 05:57:00
|
||||
2025-08-28 13:52:44+08:00,2025-08-28 13:57:25+08:00,478.0,4555.2,4559.95,-2270.5,平空,66c5bcfb-02ef-4a8c-a314-bd9d672decb5,dd5f8c6a-74da-4984-aa24-eebfbceb6d22,2025-08-28 05:57:00
|
||||
2025-08-28 14:40:01+08:00,2025-08-28 14:41:47+08:00,874.0,4572.59,4578.98,-5584.859999999491,平空,1b1efa90-7dd5-4b5f-a170-eb87df7fb7db,26b0c5e5-f4a4-4f47-9fdc-12c6ecd05704,2025-08-28 06:41:00
|
||||
2025-08-28 15:05:18+08:00,2025-08-28 15:07:11+08:00,60.0,4582.63,4577.98,-279.00000000003274,平多,61f7b24d-18b1-44ee-bc9e-60666e4b2346,e53afe94-f3fa-42c1-9e49-619cd77261ab,2025-08-28 07:07:00
|
||||
2025-08-28 15:05:18+08:00,2025-08-28 15:07:11+08:00,600.0,4582.63,4578.03,-2760.0000000002183,平多,61f7b24d-18b1-44ee-bc9e-60666e4b2346,b9cf7c9c-d909-4dad-ae66-64aa0706566f,2025-08-28 07:07:00
|
||||
2025-08-28 15:05:18+08:00,2025-08-28 15:07:11+08:00,212.0,4582.63,4577.52,-1083.3199999999306,平多,61f7b24d-18b1-44ee-bc9e-60666e4b2346,05d46d57-d923-4d18-8c18-5a49927ac910,2025-08-28 07:07:00
|
||||
2025-08-28 15:22:41+08:00,2025-08-28 15:23:11+08:00,600.0,4572.88,4567.91,-2982.000000000153,平多,184c5b21-ed98-43c2-8003-8cc5f8c1bb0a,10d151ec-2891-44fe-8527-89643b3f3b46,2025-08-28 07:23:00
|
||||
2025-08-28 15:22:41+08:00,2025-08-28 15:23:11+08:00,274.0,4572.88,4567.53,-1465.9000000000997,平多,184c5b21-ed98-43c2-8003-8cc5f8c1bb0a,2a5ef255-f800-4e98-a10c-ca3ed33bb372,2025-08-28 07:23:00
|
||||
2025-08-28 15:57:55+08:00,2025-08-28 16:02:09+08:00,547.0,4589.48,4607.47,9840.530000000377,平多,6fdd3eb9-13a9-4e2a-90b6-78e7d71f3fbc,81bc2d5e-8099-4d48-b968-d695a7f1fee4,2025-08-28 08:02:00
|
||||
2025-08-28 15:57:55+08:00,2025-08-28 16:02:09+08:00,324.0,4589.48,4607.47,5828.760000000224,平多,aec18848-d3b5-4786-8c51-a4fdfc454ee2,81bc2d5e-8099-4d48-b968-d695a7f1fee4,2025-08-28 08:02:00
|
||||
2025-08-28 16:05:54+08:00,2025-08-28 16:09:19+08:00,866.0,4614.86,4624.89,8685.980000000567,平多,8e6d3e81-c813-4759-b608-9ed4e547b24d,25509f73-848d-4620-b662-f7f93d76becc,2025-08-28 08:09:00
|
||||
2025-08-28 16:09:50+08:00,2025-08-28 16:15:00+08:00,865.0,4621.56,4617.66,-3373.500000000472,平多,29739f4f-b289-4a1e-a5e7-f6925dd72b4c,7e4c74ac-902f-47c5-9e0a-b2ef07f31e8e,2025-08-28 08:15:00
|
||||
2025-08-28 16:23:59+08:00,2025-08-28 16:28:31+08:00,867.0,4609.17,4602.01,6207.719999999874,平空,6e8dc270-512a-4826-84c9-cfdf82c6cd59,0df8cd52-9db6-4532-99e1-923fcd9c134d,2025-08-28 08:28:00
|
||||
2025-08-28 16:54:47+08:00,2025-08-28 16:58:41+08:00,870.0,4595.28,4599.69,-3836.6999999998734,平空,35598fc3-567f-4ee2-a9fe-db14aab8228b,9777e233-baa8-4238-8f48-ef3e303ff883,2025-08-28 08:58:00
|
||||
2025-08-28 17:06:53+08:00,2025-08-28 17:15:11+08:00,870.0,4595.19,4592.66,2201.0999999997784,平空,3432672f-ced3-401f-981d-7686ddd396e6,bf9ffdef-56bd-4d35-9eef-f92a668d5829,2025-08-28 09:15:00
|
||||
2025-08-28 17:43:59+08:00,2025-08-28 17:49:12+08:00,258.0,4584.79,4581.95,732.7200000000375,平空,c0b26494-b8c4-434e-bc49-e3b317c1fa88,ffbc0ba2-aa7f-4377-bade-cf31e3697b19,2025-08-28 09:49:00
|
||||
2025-08-28 17:43:59+08:00,2025-08-28 17:49:12+08:00,114.0,4584.79,4581.95,323.7600000000166,平空,6fdb9fc7-e0f4-4033-896b-a13160b4fd18,ffbc0ba2-aa7f-4377-bade-cf31e3697b19,2025-08-28 09:49:00
|
||||
2025-08-28 17:43:59+08:00,2025-08-28 17:49:12+08:00,500.0,4584.79,4581.95,1420.0000000000728,平空,b064c485-653b-4f88-a0d1-86961284dc70,ffbc0ba2-aa7f-4377-bade-cf31e3697b19,2025-08-28 09:49:00
|
||||
2025-08-28 17:49:44+08:00,2025-08-28 17:50:54+08:00,500.0,4579.81,4584.85,-2519.999999999982,平空,1f97429c-1de1-467a-8985-8f9bb17000b5,cf389028-3cfb-4521-bf8c-59c3d9782e85,2025-08-28 09:50:00
|
||||
2025-08-28 17:49:44+08:00,2025-08-28 17:50:54+08:00,139.0,4579.81,4584.85,-700.559999999995,平空,989c44a3-a81f-4939-ab13-d9884d845a65,cf389028-3cfb-4521-bf8c-59c3d9782e85,2025-08-28 09:50:00
|
||||
2025-08-28 17:49:44+08:00,2025-08-28 17:50:54+08:00,234.0,4579.58,4584.85,-1233.1800000001022,平空,0b83f7ef-0985-4168-a211-d009f6c6ff1a,cf389028-3cfb-4521-bf8c-59c3d9782e85,2025-08-28 09:50:00
|
||||
2025-08-28 18:36:51+08:00,2025-08-28 18:43:09+08:00,533.0,4609.97,4604.0,-3182.0100000001357,平多,b8391ca4-74b4-41bc-85cd-b4c82cf6e8f3,2926d3da-8a19-4fae-938f-e2556e994376,2025-08-28 10:43:00
|
||||
2025-08-28 18:36:51+08:00,2025-08-28 18:43:09+08:00,84.0,4610.0,4604.0,-504.0,平多,e441f55d-693a-4301-b6c9-10d54ab10357,2926d3da-8a19-4fae-938f-e2556e994376,2025-08-28 10:43:00
|
||||
2025-08-28 18:36:51+08:00,2025-08-28 18:43:09+08:00,33.0,4609.97,4604.0,-197.0100000000084,平多,9dbce008-1d12-422c-80f8-a2c928610074,2926d3da-8a19-4fae-938f-e2556e994376,2025-08-28 10:43:00
|
||||
2025-08-28 19:46:14+08:00,2025-08-28 19:54:40+08:00,654.0,4586.91,4591.36,-2910.299999999881,平空,005b10f4-329d-4d34-8263-b8f63f526ac8,ea07e34c-cd41-4c80-b13f-6b8308257b92,2025-08-28 11:54:00
|
||||
2025-08-28 20:30:04+08:00,2025-08-28 20:30:34+08:00,517.0,4587.25,4588.6,697.9500000001881,平多,26fe1b1b-f4f0-4c49-b6d0-f8445d4e996e,9011ad7e-ef83-4c62-ac90-3e538965ad3f,2025-08-28 12:30:00
|
||||
2025-08-28 20:30:05+08:00,2025-08-28 20:30:34+08:00,354.0,4587.44,4588.6,410.64000000027045,平多,32f1db9d-6a9b-413f-abba-3f698a83ec65,9011ad7e-ef83-4c62-ac90-3e538965ad3f,2025-08-28 12:30:00
|
||||
2025-08-28 20:31:22+08:00,2025-08-28 20:31:28+08:00,400.0,4583.96,4585.9,-775.9999999998399,平空,dd7d8a55-c4bf-4ff8-82b9-ec8591888b0d,ca1e611c-1ba6-4418-9f86-8e492a7726c5,2025-08-28 12:31:00
|
||||
2025-08-28 20:31:22+08:00,2025-08-28 20:31:29+08:00,472.0,4583.96,4585.9,-915.6799999998111,平空,dd7d8a55-c4bf-4ff8-82b9-ec8591888b0d,04792496-7da0-4a9b-95cf-293b41d8292a,2025-08-28 12:31:00
|
||||
2025-08-28 20:43:41+08:00,2025-08-28 20:46:26+08:00,92.0,4609.77,4603.6,-567.6400000000067,平多,da034c24-f333-4197-ae9d-7dfa8639be45,9d59670d-a478-4432-8ec0-e3a8c4ec4d8a,2025-08-28 12:46:00
|
||||
2025-08-28 20:43:41+08:00,2025-08-28 20:46:26+08:00,600.0,4609.77,4603.6,-3702.0000000000437,平多,da034c24-f333-4197-ae9d-7dfa8639be45,e83ff9d3-3078-4f5c-8e6a-fa5750c97b55,2025-08-28 12:46:00
|
||||
2025-08-28 20:43:41+08:00,2025-08-28 20:46:27+08:00,175.0,4609.77,4604.08,-995.7500000000891,平多,da034c24-f333-4197-ae9d-7dfa8639be45,433dc03b-8b85-4951-9f21-dfaa5d6a0de5,2025-08-28 12:46:00
|
||||
2025-08-29 09:45:36+08:00,2025-08-29 09:49:22+08:00,447.0,4466.85,4458.15,3888.9000000003252,平空,87d823b2-675d-4501-bb36-b40eef344b7c,28bb7706-f2ce-480e-b606-fc5073ce4153,2025-08-29 01:49:00
|
||||
2025-08-29 10:13:26+08:00,2025-08-29 10:18:05+08:00,2.0,4470.7,4473.08,4.760000000000218,平多,6d7fe294-0553-46ac-9179-e22446e727dc,b50fd6a4-865b-4cd4-8b83-015dde307912,2025-08-29 02:18:00
|
||||
2025-08-29 10:13:27+08:00,2025-08-29 10:18:05+08:00,445.0,4470.71,4473.08,1054.6499999999514,平多,3d441a61-f852-4d4d-88d9-a2c4494e3a5f,b50fd6a4-865b-4cd4-8b83-015dde307912,2025-08-29 02:18:00
|
||||
2025-08-29 10:21:04+08:00,2025-08-29 10:25:10+08:00,446.0,4479.68,4483.07,1511.9399999997404,平多,9ac71ec9-58a8-43af-83ca-cac772d44dce,7db1d0e0-cdda-45a4-af09-9ec2f6a830a5,2025-08-29 02:25:00
|
||||
2025-08-29 11:08:08+08:00,2025-08-29 11:10:24+08:00,446.0,4477.82,4481.35,1574.380000000292,平多,4c447405-283c-40f7-bc05-42ae2aeaaaeb,de3c12d5-ba88-4b3d-8e8a-2158228465cb,2025-08-29 03:10:00
|
||||
2025-08-29 11:25:26+08:00,2025-08-29 11:31:14+08:00,446.0,4478.16,4482.39,1886.580000000211,平多,877eb021-8a47-4d55-8efd-789fde87699c,33addd17-0151-418d-b11e-d68a26c179e6,2025-08-29 03:31:00
|
||||
2025-08-29 11:35:59+08:00,2025-08-29 11:43:49+08:00,446.0,4484.05,4494.74,4767.7399999998215,平多,913408fa-2541-48c8-a8a0-d352b8b74a93,a7fd376e-dd1c-4350-8c58-1913d130902c,2025-08-29 03:43:00
|
||||
2025-08-29 11:55:41+08:00,2025-08-29 12:00:52+08:00,444.0,4498.11,4489.03,4031.5199999999677,平空,6450e48b-f901-4907-b065-18e073f86534,7cfe2e33-9b17-4785-9e45-dd5654c483c2,2025-08-29 04:00:00
|
||||
2025-08-29 12:54:59+08:00,2025-08-29 12:58:10+08:00,670.0,4477.15,4471.43,3832.3999999995613,平空,13bb04ff-529a-48f4-a062-bdbda01abe9a,01a6ebd4-6c63-4e58-9f52-4317a83a6c27,2025-08-29 04:58:00
|
||||
2025-08-29 13:05:31+08:00,2025-08-29 13:06:05+08:00,671.0,4466.57,4472.34,-3871.670000000293,平空,5c439227-9432-48c1-8c70-184e65586599,398e954e-0534-4f4c-bdda-5d14d858afae,2025-08-29 05:06:00
|
||||
2025-08-29 13:17:39+08:00,2025-08-29 13:19:35+08:00,448.0,4461.42,4468.3,-3082.240000000049,平空,d770f2c0-6aed-4bc2-bee2-f98209775454,14ce65ce-8af2-4c07-8441-bdeea724197e,2025-08-29 05:19:00
|
||||
2025-08-29 13:59:25+08:00,2025-08-29 14:01:39+08:00,46.0,4476.01,4484.33,382.7199999999866,平多,1f80cf00-8910-42af-a7ac-dd2199579d04,96e160f6-0a35-4ac4-9f3e-295065a56092,2025-08-29 06:01:00
|
||||
2025-08-29 13:59:25+08:00,2025-08-29 14:01:39+08:00,400.0,4476.01,4484.33,3327.9999999998836,平多,1f80cf00-8910-42af-a7ac-dd2199579d04,db2ea541-8ece-4876-baa0-f803629d4675,2025-08-29 06:01:00
|
||||
2025-08-29 14:15:38+08:00,2025-08-29 14:18:12+08:00,448.0,4457.41,4451.26,2755.199999999837,平空,2616d754-2ae8-43c0-a382-46901e0dba21,913e0886-23e4-467d-932d-79e960df2947,2025-08-29 06:18:00
|
||||
2025-08-29 14:37:31+08:00,2025-08-29 14:39:31+08:00,449.0,4451.44,4451.34,-44.89999999975498,平多,9f225f1e-cdc5-42cc-8c22-6d39f68da6d6,db5c616d-faf7-4183-8e46-b95f09dbad99,2025-08-29 06:39:00
|
||||
2025-08-29 14:41:21+08:00,2025-08-29 14:44:17+08:00,449.0,4451.53,4455.7,1872.3300000000327,平多,5ba24885-5747-405a-8fa9-6a9749dbe6b4,f7e02df2-857b-4e3a-a6aa-c0d7362cb5bb,2025-08-29 06:44:00
|
||||
2025-08-29 15:01:14+08:00,2025-08-29 15:02:16+08:00,449.0,4448.83,4428.34,9200.009999999902,平空,a605bd22-e12e-44a4-9e1c-87c145bc5aa4,365ff72f-891a-4314-a005-d320d2074d5f,2025-08-29 07:02:00
|
||||
2025-08-29 15:12:00+08:00,2025-08-29 15:15:04+08:00,453.0,4412.37,4393.89,8371.439999999802,平空,03169227-80cc-4c41-89e5-7cbc59174c90,7939f969-7566-453d-a718-dfa6b96ee987,2025-08-29 07:15:00
|
||||
2025-08-29 15:44:10+08:00,2025-08-29 15:51:01+08:00,683.0,4387.22,4391.19,-2711.5099999995527,平空,e2151c19-3f56-4239-8368-77af88a0c2c1,f90b5024-cd5b-44d5-a140-4d6253d49d6a,2025-08-29 07:51:00
|
||||
2025-08-29 15:57:12+08:00,2025-08-29 16:00:46+08:00,400.0,4383.78,4387.09,-1324.00000000016,平空,f2affaff-2beb-481f-b346-c9453422f536,70cc7f5d-b472-40a1-b00b-7e4b032722fc,2025-08-29 08:00:00
|
||||
2025-08-29 15:57:12+08:00,2025-08-29 16:00:46+08:00,284.0,4383.78,4387.09,-940.0400000001137,平空,f2affaff-2beb-481f-b346-c9453422f536,676c76cd-7afa-40db-a0a7-89004bb9387b,2025-08-29 08:00:00
|
||||
2025-08-29 16:08:15+08:00,2025-08-29 16:11:24+08:00,383.0,4386.41,4376.76,3695.9499999998607,平空,da4365c6-5bed-4bff-824c-580b3be527bc,0fdf84b4-d9bd-419e-8fd0-037e8631eefd,2025-08-29 08:11:00
|
||||
2025-08-29 16:08:15+08:00,2025-08-29 16:11:24+08:00,300.0,4386.5,4376.76,2921.9999999999345,平空,940d2d22-8103-4bdd-8eaa-ade1277111da,0fdf84b4-d9bd-419e-8fd0-037e8631eefd,2025-08-29 08:11:00
|
||||
2025-08-29 16:17:21+08:00,2025-08-29 16:17:29+08:00,187.0,4364.59,4365.41,-153.33999999994558,平空,89b93201-dfd2-408c-a5de-b6ecd72424cd,db922d70-7a90-4cf4-9a17-a598fe213f8b,2025-08-29 08:17:00
|
||||
2025-08-29 16:17:21+08:00,2025-08-29 16:17:29+08:00,400.0,4364.59,4365.41,-327.9999999998836,平空,89b93201-dfd2-408c-a5de-b6ecd72424cd,574a0e60-b26e-46bf-a696-cae5868b7af2,2025-08-29 08:17:00
|
||||
2025-08-29 16:17:21+08:00,2025-08-29 16:17:29+08:00,100.0,4365.0,4365.41,-40.99999999998545,平空,e6be51d9-e746-45dd-8522-877cf7eafd33,574a0e60-b26e-46bf-a696-cae5868b7af2,2025-08-29 08:17:00
|
||||
2025-08-29 16:20:18+08:00,2025-08-29 16:20:36+08:00,688.0,4359.92,4362.29,-1630.559999999925,平空,d302895c-7318-4851-838d-396e7100fc09,719a6eff-a759-4947-8f0a-45f2ac94dfc4,2025-08-29 08:20:00
|
||||
2025-08-29 16:29:12+08:00,2025-08-29 16:33:21+08:00,527.0,4355.55,4362.63,3731.1599999999617,平多,f587dcce-e557-4b34-a55f-bf449234faca,e833feb6-8ba7-4fa5-adea-678f690e16b3,2025-08-29 08:33:00
|
||||
2025-08-29 16:29:12+08:00,2025-08-29 16:33:21+08:00,161.0,4355.55,4362.63,1139.8799999999883,平多,22c48502-01b1-4efd-9809-3bc8487b0a9b,e833feb6-8ba7-4fa5-adea-678f690e16b3,2025-08-29 08:33:00
|
||||
2025-08-29 16:37:39+08:00,2025-08-29 16:40:08+08:00,688.0,4356.21,4352.88,2291.03999999995,平空,b209c4ae-8c33-4ba5-ad48-7559d1eafc6a,f7b8931f-1568-4387-a132-8ac6f7f34891,2025-08-29 08:40:00
|
||||
2025-08-29 16:40:40+08:00,2025-08-29 16:41:12+08:00,140.0,4348.53,4353.19,-652.3999999999796,平空,9f88809c-8f0c-4dd4-b83c-552c6b78da3a,b2c3aa90-dcc1-469d-bf31-6c74b8e4099b,2025-08-29 08:41:00
|
||||
2025-08-29 16:40:40+08:00,2025-08-29 16:41:12+08:00,549.0,4348.53,4353.19,-2558.33999999992,平空,3efb68fb-7389-44fd-b48a-d749af57e70f,b2c3aa90-dcc1-469d-bf31-6c74b8e4099b,2025-08-29 08:41:00
|
||||
2025-08-29 18:24:56+08:00,2025-08-29 18:28:40+08:00,500.0,4354.11,4357.4,1644.9999999999818,平多,5b3555c7-8c22-45ad-a4b5-ef6b586335d5,88db631a-7f3b-4b85-9887-94f906259583,2025-08-29 10:28:00
|
||||
2025-08-29 18:24:56+08:00,2025-08-29 18:28:40+08:00,189.0,4354.11,4357.4,621.8099999999931,平多,f6c1a4f1-6aa0-4645-b991-503a320913be,6132d1a3-6d55-4517-b5ba-115198948b85,2025-08-29 10:28:00
|
||||
2025-08-29 18:30:45+08:00,2025-08-29 18:49:35+08:00,189.0,4351.51,4343.72,1472.3099999999931,平空,b67b7a3a-cf62-4731-bc9c-1d333f42e639,2d4f2adb-d806-4f37-8ff1-a46eddccc6b1,2025-08-29 10:49:00
|
||||
2025-08-29 18:30:45+08:00,2025-08-29 18:49:35+08:00,500.0,4351.51,4343.72,3894.999999999982,平空,0acabab2-2ab4-4921-82a0-df806d4eb40d,2d4f2adb-d806-4f37-8ff1-a46eddccc6b1,2025-08-29 10:49:00
|
||||
2025-08-29 18:58:56+08:00,2025-08-29 19:02:25+08:00,690.0,4345.89,4340.99,-3381.0000000003765,平多,b525febe-c7eb-4db9-a4f8-4a09406c9f7d,51ddc961-238a-4c9d-b985-e6ad6ef2aac7,2025-08-29 11:02:00
|
||||
2025-08-29 19:14:13+08:00,2025-08-29 19:18:33+08:00,689.0,4351.49,4346.64,-3341.649999999624,平多,da8dccbd-f79c-476b-9471-d28b719da66f,cfbfe97f-e085-41db-8679-58574bd165fd,2025-08-29 11:18:00
|
||||
2025-08-29 19:49:41+08:00,2025-08-29 19:51:55+08:00,690.0,4345.68,4340.66,-3463.800000000301,平多,98c03fb1-dca8-43ef-9ca7-0fe6d842946b,e0c7b8be-c842-412e-bb4f-e43b51fa53bb,2025-08-29 11:51:00
|
||||
2025-08-29 20:22:08+08:00,2025-08-29 20:30:35+08:00,687.0,4365.85,4411.23,31176.059999999452,平多,7ab49fe4-9bd7-4368-bb45-ffe9970e4999,c3d6e447-288e-48d1-8977-ccceda6a2e41,2025-08-29 12:30:00
|
||||
2025-08-29 20:31:05+08:00,2025-08-29 20:31:21+08:00,16.0,4439.0,4448.35,149.60000000000582,平多,76a58764-fd12-4fb4-8ee9-407aa2213a12,e3f58dbc-6a93-4c16-9dc3-7e351493f00f,2025-08-29 12:31:00
|
||||
2025-08-29 20:31:05+08:00,2025-08-29 20:31:21+08:00,659.0,4439.0,4448.35,6161.65000000024,平多,cdd4a741-4195-4fac-9896-35ee5c09a7b9,e3f58dbc-6a93-4c16-9dc3-7e351493f00f,2025-08-29 12:31:00
|
||||
2025-08-29 20:31:46+08:00,2025-08-29 20:33:05+08:00,180.0,4409.9,4406.3,-647.9999999999018,平多,a47f68a7-9565-4ea7-b3ab-770d376ffcea,46e071a8-871b-4373-92a5-8cd6cd46cc56,2025-08-29 12:33:00
|
||||
2025-08-29 20:31:46+08:00,2025-08-29 20:33:05+08:00,500.0,4409.9,4406.3,-1799.9999999997272,平多,a47f68a7-9565-4ea7-b3ab-770d376ffcea,c67209e2-0598-443d-85fa-752bdae6059b,2025-08-29 12:33:00
|
||||
2025-08-29 20:43:25+08:00,2025-08-29 20:44:07+08:00,684.0,4382.62,4386.13,-2400.8400000001493,平空,af3305f0-ec39-48fe-bf71-ac9acb281b25,7556c209-5046-4e8b-8682-99dc36803a62,2025-08-29 12:44:00
|
||||
2025-08-29 20:45:44+08:00,2025-08-29 20:49:11+08:00,102.0,4389.13,4407.06,1828.8600000000297,平多,a19435e4-fbf9-4217-bc5d-d7fc07d9818f,cc0577e9-e4b6-45fd-bf5a-47ccf6aec320,2025-08-29 12:49:00
|
||||
2025-08-29 20:45:44+08:00,2025-08-29 20:49:11+08:00,581.0,4389.13,4407.06,10417.33000000017,平多,a19435e4-fbf9-4217-bc5d-d7fc07d9818f,4fa96e6c-6692-426b-beca-c138c8505a82,2025-08-29 12:49:00
|
||||
2025-08-29 20:49:46+08:00,2025-08-29 20:53:14+08:00,500.0,4406.49,4406.02,234.99999999967258,平空,d11f3a55-a1d8-41ff-a979-778c232459c9,b2de5ff9-9281-4c2e-86bd-a071a5e1e864,2025-08-29 12:53:00
|
||||
2025-08-29 20:49:46+08:00,2025-08-29 20:53:14+08:00,180.0,4406.49,4406.02,84.59999999988213,平空,d11f3a55-a1d8-41ff-a979-778c232459c9,a4e3c4e5-4280-417c-8018-8386a63c318a,2025-08-29 12:53:00
|
||||
2025-08-29 21:00:24+08:00,2025-08-29 21:00:33+08:00,680.0,4408.35,4405.11,-2203.20000000047,平多,26e94954-9b31-48a9-9b2b-cb08da0f8e3e,14772435-8e33-4ee5-a87c-81342a7f8449,2025-08-29 13:00:00
|
||||
2025-08-29 21:01:47+08:00,2025-08-29 21:06:54+08:00,39.0,4406.15,4391.41,574.8599999999915,平空,8665f24a-3e13-4d3f-ad28-a702c79c73ed,7addab21-fcf3-4921-97ab-d77462067550,2025-08-29 13:06:00
|
||||
2025-08-29 21:01:47+08:00,2025-08-29 21:06:54+08:00,141.0,4406.15,4391.41,2078.339999999969,平空,0170663d-0cf1-43a4-8595-4511c59b3107,7addab21-fcf3-4921-97ab-d77462067550,2025-08-29 13:06:00
|
||||
2025-08-29 21:01:47+08:00,2025-08-29 21:06:54+08:00,500.0,4406.15,4391.41,7369.999999999891,平空,0170663d-0cf1-43a4-8595-4511c59b3107,3b754022-7c7c-4b14-8251-c2da4478df27,2025-08-29 13:06:00
|
||||
2025-08-29 21:10:41+08:00,2025-08-29 21:11:27+08:00,14.0,4400.55,4392.88,-107.38000000000102,平多,37e79409-f3ed-4e62-811c-6cc437396a41,f3a43d61-4d64-4d82-aada-31b38d441baa,2025-08-29 13:11:00
|
||||
2025-08-29 21:10:41+08:00,2025-08-29 21:11:27+08:00,67.0,4400.55,4392.79,-519.9200000000146,平多,37e79409-f3ed-4e62-811c-6cc437396a41,86bfa728-af41-4e41-9e87-d6ef88b1a063,2025-08-29 13:11:00
|
||||
2025-08-29 21:10:41+08:00,2025-08-29 21:11:27+08:00,600.0,4400.55,4392.88,-4602.000000000044,平多,37e79409-f3ed-4e62-811c-6cc437396a41,af01f0d0-8844-4267-8476-4000d0b2dd8b,2025-08-29 13:11:00
|
||||
2025-08-29 21:30:11+08:00,2025-08-29 21:34:27+08:00,72.0,4386.05,4373.6,896.3999999999869,平空,fecb81e1-3b36-4ae6-91ad-b6a2616ee608,aee7c322-ba5d-4b89-8b93-1c50ce5be75b,2025-08-29 13:34:00
|
||||
2025-08-29 21:30:11+08:00,2025-08-29 21:34:27+08:00,211.0,4386.05,4372.44,2871.710000000123,平空,fecb81e1-3b36-4ae6-91ad-b6a2616ee608,e20951ed-ff4e-4a06-8bb5-8112a72e38bf,2025-08-29 13:34:00
|
||||
2025-08-29 21:30:11+08:00,2025-08-29 21:34:27+08:00,99.0,4386.05,4372.44,1347.3900000000576,平空,fecb81e1-3b36-4ae6-91ad-b6a2616ee608,16329965-69b4-42a4-8145-79d1a0d2e92b,2025-08-29 13:34:00
|
||||
2025-08-29 21:30:11+08:00,2025-08-29 21:34:27+08:00,101.0,4386.29,4372.44,1398.8500000000367,平空,f1e4b54c-d392-4dc4-8f73-40a8733b85fe,16329965-69b4-42a4-8145-79d1a0d2e92b,2025-08-29 13:34:00
|
||||
2025-08-29 21:30:11+08:00,2025-08-29 21:34:27+08:00,200.0,4386.29,4372.44,2770.0000000000728,平空,f1e4b54c-d392-4dc4-8f73-40a8733b85fe,1b66a876-041a-480e-a9d6-428a3e301ec4,2025-08-29 13:34:00
|
||||
2025-08-30 08:15:30+08:00,2025-08-30 08:25:24+08:00,400.0,4343.62,4355.47,4740.0000000001455,平多,fb636839-9fb2-4e2f-bdb0-14bd57ade58a,6b8d4b73-132c-4c86-92f9-2a5df18943fd,2025-08-30 00:25:00
|
||||
2025-08-30 08:15:30+08:00,2025-08-30 08:25:24+08:00,290.0,4343.62,4355.47,3436.5000000001055,平多,fb636839-9fb2-4e2f-bdb0-14bd57ade58a,a744bd39-3c81-4cb1-a153-60ed71d6748f,2025-08-30 00:25:00
|
||||
2025-08-30 08:32:12+08:00,2025-08-30 08:35:26+08:00,64.0,4342.48,4337.98,-288.0,平多,e6185c1c-debd-4a9c-928c-edd28b360578,401ccb35-f82c-4df2-9a29-f9cd57481767,2025-08-30 00:35:00
|
||||
2025-08-30 08:32:12+08:00,2025-08-30 08:35:26+08:00,563.0,4342.48,4335.64,-3850.91999999957,平多,e6185c1c-debd-4a9c-928c-edd28b360578,69d6c400-d534-47b6-b2a8-8da09b2986aa,2025-08-30 00:35:00
|
||||
2025-08-30 08:32:12+08:00,2025-08-30 08:35:26+08:00,63.0,4342.48,4335.73,-425.25,平多,e6185c1c-debd-4a9c-928c-edd28b360578,4aa4a001-f8b0-4aea-93c3-546c71b81ee0,2025-08-30 00:35:00
|
||||
2025-08-30 08:37:25+08:00,2025-08-30 08:41:59+08:00,691.0,4336.85,4332.05,3316.8000000001257,平空,5cc6dd95-ec96-4864-a085-95b875673f0f,07f9b455-a026-41df-95c6-f9cf37f23a90,2025-08-30 00:41:00
|
||||
2025-08-30 09:26:28+08:00,2025-08-30 09:29:48+08:00,293.0,4326.54,4324.64,556.6999999998934,平空,ce7db548-7de1-448e-92c9-25139a5f23a4,70062cb1-9074-49a9-8e7f-b5b7bae639d4,2025-08-30 01:29:00
|
||||
2025-08-30 09:26:28+08:00,2025-08-30 09:29:48+08:00,400.0,4326.54,4323.46,1231.999999999971,平空,ce7db548-7de1-448e-92c9-25139a5f23a4,6145a0a3-248c-4822-8e8c-5efb6daf7d24,2025-08-30 01:29:00
|
||||
2025-08-30 09:51:41+08:00,2025-08-30 09:55:09+08:00,177.0,4312.57,4289.19,4138.260000000019,平空,4b786a41-410d-4c93-b1c6-30b118f9e7f2,f78be76a-092e-4e0e-b8c3-26867d78c1de,2025-08-30 01:55:00
|
||||
2025-08-30 09:51:41+08:00,2025-08-30 09:55:09+08:00,518.0,4312.57,4289.19,12110.840000000057,平空,96fbc46a-1dd3-4fc6-9e14-4a032e9464bf,f78be76a-092e-4e0e-b8c3-26867d78c1de,2025-08-30 01:55:00
|
||||
2025-08-30 09:55:49+08:00,2025-08-30 09:55:50+08:00,700.0,4282.58,4282.19,-273.0000000002292,平多,201dbee0-8f0d-427d-95d5-ef84e08a8aab,37ff5d0c-e10e-480c-91ca-0506e61330b6,2025-08-30 01:55:00
|
||||
2025-08-30 09:58:19+08:00,2025-08-30 09:58:59+08:00,700.0,4281.59,4280.23,-952.0000000004075,平多,0e651112-db3b-402d-a268-1ea200cf8729,65b07548-3473-4570-be22-0172c49bdb94,2025-08-30 01:58:00
|
||||
2025-08-30 10:00:09+08:00,2025-08-30 10:00:52+08:00,703.0,4264.04,4268.35,-3029.9300000002813,平空,d9703fdd-ea02-410d-8303-d1d13b94748f,17fca37e-fc89-48a9-92ed-671ab0b9088e,2025-08-30 02:00:00
|
||||
2025-08-30 10:01:23+08:00,2025-08-30 10:02:02+08:00,110.0,4268.54,4271.97,-377.300000000032,平空,022ce461-b78f-4127-9157-8c34a5e9272e,89468587-89ce-49cc-a9e3-343e24cb8036,2025-08-30 02:02:00
|
||||
2025-08-30 10:01:23+08:00,2025-08-30 10:02:02+08:00,592.0,4268.54,4271.97,-2030.5600000001723,平空,9bdcfab5-9b8e-447c-b4ac-a82724e2774d,89468587-89ce-49cc-a9e3-343e24cb8036,2025-08-30 02:02:00
|
||||
2025-08-30 10:02:55+08:00,2025-08-30 10:04:26+08:00,400.0,4275.14,4280.56,-2168.000000000029,平空,1b938bf4-dc99-4d2f-9e0b-c2fa5092d19d,4e6c40d8-e7c7-45ac-800e-5f71611479f1,2025-08-30 02:04:00
|
||||
2025-08-30 10:02:55+08:00,2025-08-30 10:04:26+08:00,141.0,4275.14,4280.56,-764.2200000000103,平空,1b938bf4-dc99-4d2f-9e0b-c2fa5092d19d,864390a3-33dd-4a47-a3c2-471b8a0104f1,2025-08-30 02:04:00
|
||||
2025-08-30 10:02:55+08:00,2025-08-30 10:04:26+08:00,160.0,4275.14,4280.56,-867.2000000000116,平空,7eb3eaf4-30fa-495d-a74c-61d725625c42,864390a3-33dd-4a47-a3c2-471b8a0104f1,2025-08-30 02:04:00
|
||||
2025-08-30 10:11:35+08:00,2025-08-30 10:18:43+08:00,232.0,4296.88,4306.91,2326.959999999941,平多,b1a002d6-951f-4912-b8cf-19bfc98f61ba,743a1e0f-48f3-43fe-9b6a-47536fa12ce1,2025-08-30 02:18:00
|
||||
2025-08-30 10:11:35+08:00,2025-08-30 10:18:43+08:00,466.0,4296.91,4306.91,4660.0,平多,94664c85-6be6-42d8-984a-f3293ed9750f,743a1e0f-48f3-43fe-9b6a-47536fa12ce1,2025-08-30 02:18:00
|
||||
2025-08-30 10:21:54+08:00,2025-08-30 10:27:07+08:00,696.0,4307.47,4306.32,800.4000000003798,平空,641e7574-db24-4c78-8782-566b13de1e39,817cbe73-8f5f-4c27-893c-ddfe59e1890d,2025-08-30 02:27:00
|
||||
2025-08-30 10:28:32+08:00,2025-08-30 10:35:18+08:00,500.0,4305.75,4311.46,2855.000000000018,平多,dee31b34-3e7f-4b74-8774-ccd70f3cb124,d37eec35-4b90-42c7-82e7-7a56e909d268,2025-08-30 02:35:00
|
||||
2025-08-30 10:28:32+08:00,2025-08-30 10:35:18+08:00,196.0,4305.75,4311.25,1078.0,平多,dee31b34-3e7f-4b74-8774-ccd70f3cb124,9cbf242a-7e93-4a3c-b798-6e3f7a88025d,2025-08-30 02:35:00
|
||||
2025-08-30 10:48:32+08:00,2025-08-30 10:57:53+08:00,695.0,4313.05,4303.81,6421.799999999848,平空,0efcab97-a0b6-4cd6-ad7e-22911ea32fa2,57fa177a-12c5-4127-91c4-289668a191ae,2025-08-30 02:57:00
|
||||
2025-08-30 11:03:45+08:00,2025-08-30 11:10:25+08:00,296.0,4308.11,4322.99,4404.480000000032,平多,36e3b573-d4be-4d54-9af3-8190731448c9,1e9213a7-3443-4209-a26a-d3e0feeaf325,2025-08-30 03:10:00
|
||||
2025-08-30 11:03:45+08:00,2025-08-30 11:10:25+08:00,400.0,4308.11,4322.99,5952.000000000044,平多,36e3b573-d4be-4d54-9af3-8190731448c9,a344d092-7b22-4230-8ba9-0702dd353041,2025-08-30 03:10:00
|
||||
2025-08-30 11:11:04+08:00,2025-08-30 11:17:45+08:00,693.0,4323.24,4328.87,3901.5900000000756,平多,c2e861bb-6de2-4dc0-aed3-7a385c0f34b8,d786c18b-8199-4335-ad6b-6115f39e089f,2025-08-30 03:17:00
|
||||
2025-08-30 11:18:36+08:00,2025-08-30 11:23:02+08:00,400.0,4333.76,4358.0,9695.999999999913,平多,3c5242f4-5844-46a7-8b84-a6c73fed27c7,d71ce99c-8d8e-4f15-be2e-85d21e853298,2025-08-30 03:23:00
|
||||
2025-08-30 11:18:36+08:00,2025-08-30 11:23:02+08:00,134.0,4333.76,4358.0,3248.1599999999708,平多,3c5242f4-5844-46a7-8b84-a6c73fed27c7,fe3607bd-9257-42ce-a0b3-06d882cb74af,2025-08-30 03:23:00
|
||||
2025-08-30 11:18:36+08:00,2025-08-30 11:23:02+08:00,158.0,4333.76,4358.0,3829.9199999999655,平多,ce9f82f0-70a9-44b1-9175-4ee31dbc8fd6,fe3607bd-9257-42ce-a0b3-06d882cb74af,2025-08-30 03:23:00
|
||||
2025-08-30 11:52:34+08:00,2025-08-30 11:57:42+08:00,69.0,4352.62,4352.32,-20.70000000001255,平多,a7a38452-b2e4-46c1-a14c-d85c0a79b250,078499df-67ff-4bdd-acaf-4e4fcf4457c0,2025-08-30 03:57:00
|
||||
2025-08-30 11:52:34+08:00,2025-08-30 11:57:42+08:00,620.0,4352.62,4352.32,-186.00000000011278,平多,76c6328b-ae56-4313-9b73-3bc43f644ef4,078499df-67ff-4bdd-acaf-4e4fcf4457c0,2025-08-30 03:57:00
|
||||
2025-08-30 12:01:25+08:00,2025-08-30 12:07:56+08:00,500.0,4351.41,4357.44,-3014.9999999998727,平空,2228c2d3-6d7b-4835-8065-42ce6ad3a5af,3544a077-b511-45ef-b088-c380928048ee,2025-08-30 04:07:00
|
||||
2025-08-30 12:01:25+08:00,2025-08-30 12:07:56+08:00,121.0,4351.7,4357.44,-694.5399999999736,平空,e33d8985-328c-4a44-829e-b8ebf6127582,3544a077-b511-45ef-b088-c380928048ee,2025-08-30 04:07:00
|
||||
2025-08-30 12:01:25+08:00,2025-08-30 12:07:56+08:00,527.0,4351.41,4357.44,-3177.809999999866,平空,6f169ccd-3d5a-4e35-9653-7efd0a1580dc,3544a077-b511-45ef-b088-c380928048ee,2025-08-30 04:07:00
|
||||
2025-08-30 13:01:32+08:00,2025-08-30 13:07:24+08:00,577.0,4353.26,4358.47,-3006.170000000021,平空,27412351-b7db-41d2-941e-014723b46c1a,f65cc5f5-8a78-4a59-bb58-cf0af4df8448,2025-08-30 05:07:00
|
||||
2025-08-30 13:01:33+08:00,2025-08-30 13:07:24+08:00,298.0,4353.26,4358.47,-1552.5800000000108,平空,cab58243-ce8d-4149-b279-3417eac36fcd,f65cc5f5-8a78-4a59-bb58-cf0af4df8448,2025-08-30 05:07:00
|
||||
2025-08-30 13:01:33+08:00,2025-08-30 13:07:24+08:00,273.0,4353.26,4359.1,-1594.3200000000397,平空,cab58243-ce8d-4149-b279-3417eac36fcd,c28367f5-fcb9-4345-82f2-c56451e2e462,2025-08-30 05:07:00
|
||||
2025-08-30 13:20:13+08:00,2025-08-30 13:32:38+08:00,1143.0,4373.03,4377.0,-4537.710000000291,平空,0f73205b-a08a-4fad-9e77-f5d2fbbf1aee,6d5e0a1c-53e6-4fac-a76a-77cb3341a976,2025-08-30 05:32:00
|
||||
2025-08-30 13:33:46+08:00,2025-08-30 13:42:14+08:00,1140.0,4385.31,4391.97,7592.399999999834,平多,11f0898c-7cba-49da-91d9-1fc6e9b57ab2,e7744350-f030-4566-bdeb-8a76060f8066,2025-08-30 05:42:00
|
||||
2025-08-30 13:48:01+08:00,2025-08-30 13:56:36+08:00,1137.0,4395.74,4382.93,14564.96999999942,平空,d00ac585-7cde-477d-af47-a5889aa5ef70,e99ef7df-85e7-46b4-bc8a-4a6f2fc811fe,2025-08-30 05:56:00
|
||||
2025-08-30 13:58:04+08:00,2025-08-30 14:03:08+08:00,1141.0,4381.43,4385.01,-4084.779999999917,平空,76216a51-657b-4659-86cb-2295aa642dac,8a9ab27e-38f2-4ec3-bbaf-642afd20a6e5,2025-08-30 06:03:00
|
||||
2025-08-30 14:10:44+08:00,2025-08-30 14:15:13+08:00,601.0,4383.15,4377.63,3317.5199999997158,平空,587020d7-a3eb-4e44-b6c7-5c95b8c5451a,8ecfa693-c528-405b-8db4-0b02ccc7b9b3,2025-08-30 06:15:00
|
||||
2025-08-30 14:10:44+08:00,2025-08-30 14:15:13+08:00,500.0,4383.15,4377.63,2759.9999999997635,平空,587020d7-a3eb-4e44-b6c7-5c95b8c5451a,babe1585-607c-4004-9aa9-19e1f5374ac1,2025-08-30 06:15:00
|
||||
2025-08-30 14:10:44+08:00,2025-08-30 14:15:13+08:00,39.0,4383.15,4377.63,215.27999999998156,平空,587020d7-a3eb-4e44-b6c7-5c95b8c5451a,ab4e9d81-4133-42b0-9460-9a2efd36ec0b,2025-08-30 06:15:00
|
||||
2025-08-30 14:26:36+08:00,2025-08-30 14:33:14+08:00,1140.0,4384.3,4391.39,8082.600000000166,平多,f703a5cc-3428-4796-a288-bfa6442800cb,a4e60efd-9afc-48b1-8850-6054e1330780,2025-08-30 06:33:00
|
||||
2025-08-30 14:45:13+08:00,2025-08-30 14:52:41+08:00,1137.0,4395.22,4390.35,5537.189999999876,平空,b1f16517-7b15-43a4-a3e7-8f0b60f9f732,0445e9e3-c80c-4beb-a121-18b6094f52eb,2025-08-30 06:52:00
|
||||
2025-08-30 15:20:15+08:00,2025-08-30 15:40:40+08:00,52.0,4392.17,4398.92,-351.0,平空,504ad303-3016-470e-9deb-a717d25a761c,9a648f36-ef00-45a4-8e56-868ab74acce6,2025-08-30 07:40:00
|
||||
2025-08-30 15:20:15+08:00,2025-08-30 15:40:40+08:00,500.0,4392.17,4398.92,-3375.0,平空,59f674e3-aff1-4b79-b8b0-c650b299d27d,9a648f36-ef00-45a4-8e56-868ab74acce6,2025-08-30 07:40:00
|
||||
2025-08-30 15:20:15+08:00,2025-08-30 15:40:40+08:00,586.0,4392.17,4398.92,-3955.5,平空,77548f6a-59ae-4ac2-880c-ea1f5679f507,9a648f36-ef00-45a4-8e56-868ab74acce6,2025-08-30 07:40:00
|
||||
2025-08-30 15:46:04+08:00,2025-08-30 15:50:09+08:00,1134.0,4408.51,4402.78,-6497.820000000536,平多,76eb4bce-ff36-4d4c-b97a-cda6d9f7b544,63f7184c-7211-417f-b594-e39436d14940,2025-08-30 07:50:00
|
||||
2025-08-30 16:11:35+08:00,2025-08-30 16:32:13+08:00,400.0,4393.35,4389.12,1692.0000000001892,平空,be9ff2ce-0d9e-4184-8d43-c49b33c71c95,754db465-b458-43a3-b2b5-3fb4f9fc2aaf,2025-08-30 08:32:00
|
||||
2025-08-30 16:11:35+08:00,2025-08-30 16:32:13+08:00,500.0,4393.35,4389.12,2115.0000000002365,平空,cdf388db-68a2-47ea-ba94-431f02dadeac,754db465-b458-43a3-b2b5-3fb4f9fc2aaf,2025-08-30 08:32:00
|
||||
2025-08-30 16:11:35+08:00,2025-08-30 16:32:13+08:00,237.0,4393.35,4389.12,1002.5100000001121,平空,e6f8d379-c20f-4a9d-90a4-7a5904bff1f9,754db465-b458-43a3-b2b5-3fb4f9fc2aaf,2025-08-30 08:32:00
|
||||
2025-08-30 17:05:10+08:00,2025-08-30 17:11:26+08:00,133.0,4390.54,4387.01,469.48999999996613,平空,d4db2208-ba63-42c7-af51-9cec92bfe6f1,46858333-121c-4020-9567-bc44d63dec01,2025-08-30 09:11:00
|
||||
2025-08-30 17:05:10+08:00,2025-08-30 17:11:26+08:00,190.0,4390.54,4387.01,670.6999999999516,平空,d4db2208-ba63-42c7-af51-9cec92bfe6f1,4d19376a-cfb7-4da9-8859-982e073624d1,2025-08-30 09:11:00
|
||||
2025-08-30 17:05:10+08:00,2025-08-30 17:11:26+08:00,310.0,4390.54,4387.01,1094.299999999921,平空,00dda8f5-7156-4d25-9ea0-29e5fb2a4aca,4d19376a-cfb7-4da9-8859-982e073624d1,2025-08-30 09:11:00
|
||||
2025-08-30 17:05:10+08:00,2025-08-30 17:11:26+08:00,505.0,4390.54,4387.01,1782.6499999998714,平空,00dda8f5-7156-4d25-9ea0-29e5fb2a4aca,e28b9838-7c48-4c78-a369-8259844cc3f4,2025-08-30 09:11:00
|
||||
2025-08-30 17:18:39+08:00,2025-08-30 17:28:37+08:00,1139.0,4389.73,4385.08,-5296.349999999586,平多,2789c169-8a5d-47da-a6f9-6800e72ec26e,cd9481fb-33de-4e75-beb8-119ace92b5f6,2025-08-30 09:28:00
|
||||
2025-08-30 17:31:17+08:00,2025-08-30 17:37:08+08:00,1141.0,4379.77,4384.13,-4974.759999999626,平空,434fc13a-925d-4abd-8677-0cd2b1a412f0,1e63ca1f-7f77-4ed9-a885-159f5dac641e,2025-08-30 09:37:00
|
||||
2025-08-30 17:44:10+08:00,2025-08-30 17:48:40+08:00,1140.0,4384.13,4388.17,-4605.5999999999585,平空,288b4dc7-affd-4b36-897a-e02a024dbae0,70c0e310-219d-45d2-a91d-6b685af4a065,2025-08-30 09:48:00
|
||||
2025-08-30 17:50:34+08:00,2025-08-30 17:54:02+08:00,1138.0,4389.94,4393.0,3482.2800000004554,平多,b78acf6a-d3b3-44c2-b484-ad95dfedd167,9473c228-7657-44cd-9038-38aeb833c3ab,2025-08-30 09:54:00
|
||||
2025-08-30 18:05:09+08:00,2025-08-30 18:07:30+08:00,1138.0,4392.24,4394.52,2594.640000000745,平多,44cd4f02-5250-47d0-8e71-a66f856405ba,81abc1fa-1789-4f69-aaaa-2da1f3f34186,2025-08-30 10:07:00
|
||||
2025-08-31 10:28:55+08:00,2025-08-31 10:34:47+08:00,326.0,4450.3,4446.25,-1320.3000000000593,平多,0fe00795-d7d9-4586-b864-e445017c7f9d,716e265c-d39a-46b3-92e2-99f5d8a6b45f,2025-08-31 02:34:00
|
||||
2025-08-31 10:28:55+08:00,2025-08-31 10:34:47+08:00,572.0,4450.3,4446.25,-2316.600000000104,平多,0fe00795-d7d9-4586-b864-e445017c7f9d,e7b360d6-da94-4cb7-b8c7-0b10db53a03b,2025-08-31 02:34:00
|
||||
2025-08-31 11:08:10+08:00,2025-08-31 11:12:01+08:00,897.0,4454.6,4458.68,-3659.7599999999347,平空,c31d66e2-a558-4b86-a520-3a9eb9d55346,f737efdc-5818-4bc5-b3db-1da76e7caf71,2025-08-31 03:12:00
|
||||
2025-08-31 11:24:10+08:00,2025-08-31 11:27:33+08:00,508.0,4457.8,4453.59,-2138.6800000000185,平多,38413547-9d48-43fc-a2be-12d2a8e0cd9f,ce5c15c0-d938-4b48-ab08-807531847f2e,2025-08-31 03:27:00
|
||||
2025-08-31 11:24:10+08:00,2025-08-31 11:27:33+08:00,389.0,4457.8,4453.59,-1637.6900000000142,平多,816792ec-d409-4688-b7fd-2ee3e3f4491e,ce5c15c0-d938-4b48-ab08-807531847f2e,2025-08-31 03:27:00
|
||||
2025-08-31 11:49:43+08:00,2025-08-31 11:53:50+08:00,500.0,4462.28,4469.74,3730.000000000018,平多,b1e2c3bf-db41-4762-b152-7a6a469c8cdb,52c185f1-3f74-42b6-afe5-6032df9dcb11,2025-08-31 03:53:00
|
||||
2025-08-31 11:49:43+08:00,2025-08-31 11:53:50+08:00,396.0,4462.28,4469.74,2954.1600000000144,平多,b1e2c3bf-db41-4762-b152-7a6a469c8cdb,5fbbe739-8d3c-4aab-8333-7860bdca1d69,2025-08-31 03:53:00
|
||||
2025-08-31 11:54:26+08:00,2025-08-31 12:00:20+08:00,293.0,4475.79,4483.53,2267.819999999936,平多,614c6489-c5a5-451d-a2a0-699e38d5a695,5da7f47f-33e3-45b8-9dfe-8232807978fc,2025-08-31 04:00:00
|
||||
2025-08-31 11:54:26+08:00,2025-08-31 12:00:20+08:00,200.0,4475.79,4483.53,1547.9999999999563,平多,c443995b-f986-4a55-b6d9-59c43fa71fd2,5da7f47f-33e3-45b8-9dfe-8232807978fc,2025-08-31 04:00:00
|
||||
2025-08-31 11:54:26+08:00,2025-08-31 12:00:20+08:00,400.0,4475.79,4483.53,3095.9999999999127,平多,c443995b-f986-4a55-b6d9-59c43fa71fd2,7d96c3c9-b227-48f3-b9fe-9a5c08871cfe,2025-08-31 04:00:00
|
||||
2025-08-31 12:15:25+08:00,2025-08-31 12:25:03+08:00,894.0,4473.35,4467.05,5632.200000000163,平空,d5064049-73a7-4a01-8d14-3d0ba056d572,b4537133-4056-4afc-80c2-7656e28cff88,2025-08-31 04:25:00
|
||||
2025-08-31 12:30:21+08:00,2025-08-31 12:33:51+08:00,500.0,4461.85,4465.99,-2069.999999999709,平空,516d9754-0fe4-4f2e-9289-5827765f7c67,13f52cac-de10-4bba-93d5-8376ba2c5139,2025-08-31 04:33:00
|
||||
2025-08-31 12:30:21+08:00,2025-08-31 12:33:51+08:00,100.0,4461.85,4465.88,-402.99999999997453,平空,516d9754-0fe4-4f2e-9289-5827765f7c67,a77196b2-cc47-482b-8fb0-a140d25da936,2025-08-31 04:33:00
|
||||
2025-08-31 12:30:21+08:00,2025-08-31 12:33:51+08:00,296.0,4461.85,4465.99,-1225.4399999998277,平空,516d9754-0fe4-4f2e-9289-5827765f7c67,c529dbf2-2dd4-46aa-b5b6-dd0ed4f473e2,2025-08-31 04:33:00
|
||||
2025-08-31 12:42:50+08:00,2025-08-31 12:48:13+08:00,661.0,4464.2,4468.3,-2710.1000000002405,平空,0f3ef65f-e754-4229-87e6-e0f102020ce9,bce7cdc5-2c37-4bda-9b2a-05c865dd69ba,2025-08-31 04:48:00
|
||||
2025-08-31 12:42:50+08:00,2025-08-31 12:48:13+08:00,235.0,4464.2,4468.3,-963.5000000000855,平空,10e40e0b-a920-4e80-8d17-ea6f73d645e3,bce7cdc5-2c37-4bda-9b2a-05c865dd69ba,2025-08-31 04:48:00
|
||||
2025-08-31 13:31:35+08:00,2025-08-31 13:38:28+08:00,100.0,4453.99,4450.23,-376.0000000000218,平多,e65553ad-4738-4b5f-af08-c7373301aef8,ce3b836f-f3c8-4e27-bdd5-33b9e1ba2bc9,2025-08-31 05:38:00
|
||||
2025-08-31 13:31:35+08:00,2025-08-31 13:38:28+08:00,600.0,4454.98,4450.23,-2850.0,平多,57f64aba-942f-4563-a3c7-9dfdfeaf98e4,ce3b836f-f3c8-4e27-bdd5-33b9e1ba2bc9,2025-08-31 05:38:00
|
||||
2025-08-31 13:31:35+08:00,2025-08-31 13:38:28+08:00,198.0,4454.98,4450.23,-940.5,平多,597f272b-ce3c-45bf-99ee-4abe2f630972,ce3b836f-f3c8-4e27-bdd5-33b9e1ba2bc9,2025-08-31 05:38:00
|
||||
2025-08-31 14:22:05+08:00,2025-08-31 14:23:54+08:00,275.0,4441.46,4446.28,-1325.49999999992,平空,eafee2f8-8601-47c7-bfe0-2f0a35bd1411,3744b6fa-5971-44d1-885f-7c7f93da8e70,2025-08-31 06:23:00
|
||||
2025-08-31 14:22:05+08:00,2025-08-31 14:23:54+08:00,100.0,4442.21,4446.28,-406.9999999999709,平空,702cd777-dc0b-4602-8c19-618520cf46f4,3744b6fa-5971-44d1-885f-7c7f93da8e70,2025-08-31 06:23:00
|
||||
2025-08-31 14:22:05+08:00,2025-08-31 14:23:54+08:00,525.0,4441.46,4446.28,-2530.499999999847,平空,be46bffb-290a-477a-81d9-fd4e7c1d2f3c,3744b6fa-5971-44d1-885f-7c7f93da8e70,2025-08-31 06:23:00
|
||||
2025-08-31 14:33:49+08:00,2025-08-31 14:39:00+08:00,900.0,4439.81,4444.22,-3968.999999999869,平空,f25e9b2a-1bb7-4bc5-852e-5c953f222f61,2d0c6c6c-070a-498b-ade7-0235a6d3e644,2025-08-31 06:39:00
|
||||
2025-08-31 14:40:14+08:00,2025-08-31 14:47:51+08:00,411.0,4444.0,4439.28,-1939.9200000001047,平多,46e5c712-8dff-423d-805d-d09da5e43571,9a2c96f9-3c09-4cca-8539-7251e96e99df,2025-08-31 06:47:00
|
||||
2025-08-31 14:40:14+08:00,2025-08-31 14:47:51+08:00,400.0,4444.0,4439.13,-1947.9999999999563,平多,46e5c712-8dff-423d-805d-d09da5e43571,f92ff1ec-cfa2-4576-bcbb-978910e5200c,2025-08-31 06:47:00
|
||||
2025-08-31 14:40:14+08:00,2025-08-31 14:47:51+08:00,89.0,4444.0,4439.13,-433.4299999999903,平多,46e5c712-8dff-423d-805d-d09da5e43571,5f572e4d-e1a7-45cf-bc1b-b55ce56f12b4,2025-08-31 06:47:00
|
||||
2025-08-31 14:51:09+08:00,2025-08-31 14:57:17+08:00,450.0,4436.57,4435.16,634.4999999999345,平空,3d5b8a22-edc9-4076-8d61-23f8ed561202,9c959838-3125-426e-abaf-d09fc309fda2,2025-08-31 06:57:00
|
||||
2025-08-31 15:30:46+08:00,2025-08-31 15:33:17+08:00,449.0,4450.96,4446.74,-1894.7800000001143,平多,45ada65c-b8e3-4316-9414-5faf1dcad582,57564f53-121e-47ac-8804-e448bb39c82c,2025-08-31 07:33:00
|
||||
2025-08-31 16:05:31+08:00,2025-08-31 16:08:48+08:00,448.0,4461.21,4457.34,-1733.759999999951,平多,050937e7-f1e4-459e-982a-8d5263e718cb,ad5c9d31-f050-4981-9f51-7852dbf0df37,2025-08-31 08:08:00
|
||||
2025-08-31 16:20:45+08:00,2025-08-31 16:24:22+08:00,48.0,4463.0,4462.93,-3.35999999998603,平多,e41030f3-095f-4e36-b465-e7f7d20b2972,83b7259d-91f7-485a-92ee-37c94790c7f4,2025-08-31 08:24:00
|
||||
2025-08-31 16:20:45+08:00,2025-08-31 16:24:22+08:00,400.0,4463.0,4462.93,-27.999999999883585,平多,e41030f3-095f-4e36-b465-e7f7d20b2972,de316218-6bb2-48c0-b540-fc4babf5ce9b,2025-08-31 08:24:00
|
||||
2025-08-31 17:24:37+08:00,2025-08-31 17:27:28+08:00,447.0,4473.73,4467.56,2757.989999999626,平空,b8d42b12-b7c8-465d-a67b-56085ceae48f,74ed9f75-4d45-47a8-a636-5a1cbe5511ce,2025-08-31 09:27:00
|
||||
2025-08-31 17:48:57+08:00,2025-08-31 17:52:50+08:00,448.0,4463.45,4470.31,3073.2800000002608,平多,9b9c8688-3c74-48c3-b1bf-6bbc162b3edf,70a47e37-f8fb-440f-9ea6-50ffbf8055f6,2025-08-31 09:52:00
|
||||
2025-08-31 17:59:19+08:00,2025-08-31 18:00:09+08:00,152.0,4461.24,4465.19,-600.3999999999724,平空,28a00768-c1e1-42d0-b75e-f7b6b8608b60,9263141f-f5ac-4173-b3c2-b8e2f7ba0985,2025-08-31 10:00:00
|
||||
2025-08-31 17:59:19+08:00,2025-08-31 18:00:09+08:00,520.0,4461.24,4465.19,-2053.9999999999054,平空,28a00768-c1e1-42d0-b75e-f7b6b8608b60,32d89f55-ae9c-49a6-8e2a-e47b5f4167ab,2025-08-31 10:00:00
|
||||
2025-08-31 18:07:59+08:00,2025-08-31 18:12:30+08:00,672.0,4459.1,4452.94,4139.520000000513,平空,765d6b9a-0683-4dca-b76c-6e9a2ea65cd5,7f047d14-dac5-4fca-94ed-c0fdfec63578,2025-08-31 10:12:00
|
||||
2025-08-31 18:17:18+08:00,2025-08-31 18:29:14+08:00,673.0,4451.84,4455.42,2409.339999999951,平多,755ad988-d66e-4bc8-9907-b69112e3a11a,775a3323-bbd4-4e5c-ae56-d68e178eb05e,2025-08-31 10:29:00
|
||||
2025-08-31 22:07:31+08:00,2025-08-31 22:13:15+08:00,1122.0,4453.82,4450.0,4286.0399999996735,平空,c3c54d31-6a99-45ad-8139-546ac61bed34,ef4df9cc-3394-4d53-8fed-7e261a8b0c60,2025-08-31 14:13:00
|
||||
2025-08-31 22:14:11+08:00,2025-08-31 22:20:53+08:00,500.0,4453.63,4459.25,2809.9999999999454,平多,18042f61-552b-41bb-b228-8d8031ee3c14,ad7b6056-101b-49e4-9b9f-dea68880cd52,2025-08-31 14:20:00
|
||||
2025-08-31 22:14:11+08:00,2025-08-31 22:20:53+08:00,122.0,4453.63,4459.25,685.6399999999867,平多,208d18b8-941b-4ce3-aa29-2f5fb074cc01,ad7b6056-101b-49e4-9b9f-dea68880cd52,2025-08-31 14:20:00
|
||||
2025-08-31 22:14:11+08:00,2025-08-31 22:20:53+08:00,500.0,4453.63,4459.25,2809.9999999999454,平多,4e557687-e4c4-4f2d-85b6-364f6e5fd752,ad7b6056-101b-49e4-9b9f-dea68880cd52,2025-08-31 14:20:00
|
||||
2025-08-31 23:05:00+08:00,2025-08-31 23:09:16+08:00,1117.0,4473.11,4471.27,-2055.2799999991466,平多,41d06c42-ac03-418f-9451-6f5a5fdda71a,5cd65f94-490b-4bb2-bd42-d1a64942d201,2025-08-31 15:09:00
|
||||
2025-09-01 09:07:41+08:00,2025-09-01 09:11:05+08:00,400.0,4394.0,4383.39,4243.999999999869,平空,092adf53-a461-4de7-8e04-9bfe1de8c387,b3e26049-21f9-495e-9a96-7498f06dcd82,2025-09-01 01:11:00
|
||||
2025-09-01 09:07:41+08:00,2025-09-01 09:11:05+08:00,55.0,4394.0,4383.39,583.549999999982,平空,092adf53-a461-4de7-8e04-9bfe1de8c387,0614bd80-655c-49d4-b895-798f56f54803,2025-09-01 01:11:00
|
||||
2025-09-01 09:20:32+08:00,2025-09-01 09:25:17+08:00,454.0,4399.41,4396.45,-1343.8400000000165,平多,ef1554ec-bb75-4b5d-a3fd-03325d8ce248,11fb1482-9f6a-4b3f-aec4-e0e9f46ec4eb,2025-09-01 01:25:00
|
||||
2025-09-01 09:31:04+08:00,2025-09-01 09:32:03+08:00,453.0,4409.02,4405.26,-1703.2800000000989,平多,8050d64e-1c53-40e1-89e7-68481d09e689,079730bc-d8ce-413a-a77f-a889bed73972,2025-09-01 01:32:00
|
||||
2025-09-01 09:34:47+08:00,2025-09-01 09:37:04+08:00,453.0,4405.92,4417.68,5327.280000000099,平多,4f3b316a-37b6-43b9-a742-28760ce5267b,18f19970-4048-485b-b95a-00b3e804d0d9,2025-09-01 01:37:00
|
||||
2025-09-01 10:11:29+08:00,2025-09-01 10:16:05+08:00,400.0,4400.12,4391.86,3304.0000000000873,平空,9b7a45aa-4ed0-4fa2-9c77-ee71e8319ecf,6adc05fa-6727-4961-8ae9-ef7f248ce06f,2025-09-01 02:16:00
|
||||
2025-09-01 10:11:30+08:00,2025-09-01 10:16:05+08:00,54.0,4400.12,4391.86,446.0400000000118,平空,a2b8e0df-1a70-4cbe-b513-734c169a45e5,6adc05fa-6727-4961-8ae9-ef7f248ce06f,2025-09-01 02:16:00
|
||||
2025-09-01 10:29:03+08:00,2025-09-01 10:30:26+08:00,683.0,4389.33,4386.21,-2130.9599999999255,平多,9295d00e-a050-472f-b008-426e40eb0f71,73e0f83a-583a-4ae6-8dc3-41c2f37bb58e,2025-09-01 02:30:00
|
||||
2025-09-01 10:35:00+08:00,2025-09-01 10:36:12+08:00,685.0,4375.45,4379.38,-2692.0500000001994,平空,8e5b463d-f4fe-47f3-b39b-ec3721c79e7a,2f96c834-2272-491f-8bc1-8cbe6c4ea452,2025-09-01 02:36:00
|
||||
2025-09-01 11:03:26+08:00,2025-09-01 11:05:42+08:00,684.0,4383.62,4391.35,5287.3200000003235,平多,ad2ab2b0-b070-4027-ba77-039ae801c30b,ab66e717-8f29-4ebe-80c7-165f36806e5f,2025-09-01 03:05:00
|
||||
2025-09-01 11:12:18+08:00,2025-09-01 11:15:56+08:00,184.0,4382.88,4374.85,1477.5199999999531,平空,60e70f9e-3ed0-402a-a33e-0d9104ac17b6,2c200cb7-b183-474a-b78f-bba9e592c8c0,2025-09-01 03:15:00
|
||||
2025-09-01 11:12:18+08:00,2025-09-01 11:15:56+08:00,500.0,4382.88,4374.85,4014.9999999998727,平空,4f57713c-6a4c-42d4-aa6b-c03bae934835,2c200cb7-b183-474a-b78f-bba9e592c8c0,2025-09-01 03:15:00
|
||||
2025-09-01 11:34:38+08:00,2025-09-01 11:41:09+08:00,286.0,4370.18,4382.21,3440.579999999927,平多,7716be61-be89-4b30-8939-ff0c2c42b4e2,15dadfe0-75d8-4cc7-838d-931a1d21ccf8,2025-09-01 03:41:00
|
||||
2025-09-01 11:34:38+08:00,2025-09-01 11:41:09+08:00,400.0,4370.18,4382.21,4811.999999999898,平多,7716be61-be89-4b30-8939-ff0c2c42b4e2,9ae0cd54-0bfb-4a24-8c7a-1a247e078eb8,2025-09-01 03:41:00
|
||||
2025-09-01 11:54:59+08:00,2025-09-01 11:58:53+08:00,10.0,4396.0,4403.97,79.70000000000255,平多,b0876036-7e56-46cb-bddd-ccabb6519f7c,600628e2-049f-44c8-a905-46ea1135e601,2025-09-01 03:58:00
|
||||
2025-09-01 11:54:59+08:00,2025-09-01 11:58:53+08:00,145.0,4396.47,4403.97,1087.5,平多,4bc131e8-70e0-4140-a2c8-b054e0507acd,600628e2-049f-44c8-a905-46ea1135e601,2025-09-01 03:58:00
|
||||
2025-09-01 11:54:59+08:00,2025-09-01 11:58:53+08:00,355.0,4396.47,4403.97,2662.5,平多,4bc131e8-70e0-4140-a2c8-b054e0507acd,9d3eca96-2443-4c6e-b40e-1020bde26895,2025-09-01 03:58:00
|
||||
2025-09-01 11:54:59+08:00,2025-09-01 11:58:53+08:00,172.0,4396.47,4403.97,1290.0,平多,86ab0723-57a8-4e31-9000-9b73f49ad308,9d3eca96-2443-4c6e-b40e-1020bde26895,2025-09-01 03:58:00
|
||||
2025-09-01 12:19:34+08:00,2025-09-01 12:23:09+08:00,500.0,4389.68,4388.07,805.000000000291,平空,2aec9027-7a84-45c2-83d3-8c5871d90106,95aa0191-4e59-46b0-b538-e0a3fd31a029,2025-09-01 04:23:00
|
||||
2025-09-01 12:19:34+08:00,2025-09-01 12:23:09+08:00,87.0,4389.68,4388.0,146.16000000002532,平空,2aec9027-7a84-45c2-83d3-8c5871d90106,861ff638-8ec8-4a6a-bafa-74206b093040,2025-09-01 04:23:00
|
||||
2025-09-01 12:19:34+08:00,2025-09-01 12:23:09+08:00,52.0,4389.68,4388.07,83.72000000003027,平空,2aec9027-7a84-45c2-83d3-8c5871d90106,6cff4ab5-d999-4dda-87e2-067c9adbd240,2025-09-01 04:23:00
|
||||
2025-09-01 12:19:34+08:00,2025-09-01 12:23:09+08:00,500.0,4389.68,4388.07,805.000000000291,平空,2aec9027-7a84-45c2-83d3-8c5871d90106,4dc3eeb9-b54d-4820-bc73-2172e8e5fd66,2025-09-01 04:23:00
|
||||
2025-09-01 12:52:26+08:00,2025-09-01 12:56:26+08:00,1141.0,4381.97,4385.85,4427.0800000001245,平多,a3ef0945-101d-4f17-9cda-711fe44e2cf1,ab314993-0d4e-4632-8bcd-66aaaef57066,2025-09-01 04:56:00
|
||||
2025-09-01 13:11:24+08:00,2025-09-01 13:12:28+08:00,475.0,4363.3,4368.36,-2403.499999999758,平空,4d1d7cf6-26fe-44f0-be21-d10a9871ae1a,53f10353-21b1-41c1-8554-18c5bac4991a,2025-09-01 05:12:00
|
||||
2025-09-01 13:11:24+08:00,2025-09-01 13:12:28+08:00,400.0,4363.3,4368.36,-2023.9999999997963,平空,63978423-878f-4bc0-8ac6-7526d1aa3b24,53f10353-21b1-41c1-8554-18c5bac4991a,2025-09-01 05:12:00
|
||||
2025-09-01 13:11:24+08:00,2025-09-01 13:12:28+08:00,500.0,4363.3,4368.36,-2529.9999999997453,平空,9736d4ed-bfd4-4fb4-a2b9-7a335070a480,53f10353-21b1-41c1-8554-18c5bac4991a,2025-09-01 05:12:00
|
||||
2025-09-01 13:19:17+08:00,2025-09-01 13:20:04+08:00,1376.0,4357.3,4364.42,-9797.11999999985,平空,9d7b259b-c1ec-4d54-83c5-e1588347d99f,a63b68c2-d467-4b4b-992a-175e562a59cd,2025-09-01 05:20:00
|
||||
2025-09-01 13:23:06+08:00,2025-09-01 13:33:39+08:00,291.0,4390.89,4384.71,-1798.3800000000847,平多,348b7ba0-df18-4d21-b652-1c54dc5b4635,2a709f71-d330-49c0-9759-3b65a140431c,2025-09-01 05:33:00
|
||||
2025-09-01 13:23:06+08:00,2025-09-01 13:33:39+08:00,300.0,4390.89,4384.71,-1854.0000000000873,平多,c8c60f9e-a45b-4f5a-b000-a1a436757403,2a709f71-d330-49c0-9759-3b65a140431c,2025-09-01 05:33:00
|
||||
2025-09-01 13:23:06+08:00,2025-09-01 13:33:39+08:00,300.0,4390.89,4384.71,-1854.0000000000873,平多,970891fc-d2ec-4c54-a196-7a450a88c968,2a709f71-d330-49c0-9759-3b65a140431c,2025-09-01 05:33:00
|
||||
2025-09-01 13:23:06+08:00,2025-09-01 13:33:39+08:00,20.0,4390.01,4384.71,-106.00000000000364,平多,71cd000e-8c95-4f6f-a32f-1440db6e8c0d,2a709f71-d330-49c0-9759-3b65a140431c,2025-09-01 05:33:00
|
||||
2025-09-01 13:49:13+08:00,2025-09-01 13:54:15+08:00,287.0,4396.35,4396.21,-40.18000000009397,平多,83a1a14a-a4e3-40f1-86bd-915987501b35,4c298f23-9a76-4a0f-8bc1-f8ed23f12ede,2025-09-01 05:54:00
|
||||
2025-09-01 13:49:13+08:00,2025-09-01 13:54:15+08:00,200.0,4396.35,4396.21,-28.000000000065484,平多,83a1a14a-a4e3-40f1-86bd-915987501b35,46728c29-6645-408a-bba2-07f01cd5d06d,2025-09-01 05:54:00
|
||||
2025-09-01 13:49:13+08:00,2025-09-01 13:54:15+08:00,13.0,4396.35,4396.21,-1.8200000000042564,平多,83a1a14a-a4e3-40f1-86bd-915987501b35,c4cb8611-b4dd-41fd-95c2-c9455c1237e6,2025-09-01 05:54:00
|
||||
2025-09-01 13:49:13+08:00,2025-09-01 13:54:15+08:00,187.0,4396.35,4396.21,-26.180000000061227,平多,47230a4e-8ee9-4d68-aa51-01753afc623b,c4cb8611-b4dd-41fd-95c2-c9455c1237e6,2025-09-01 05:54:00
|
||||
2025-09-01 13:49:13+08:00,2025-09-01 13:54:15+08:00,213.0,4396.35,4396.13,-46.86000000005424,平多,47230a4e-8ee9-4d68-aa51-01753afc623b,e06fcad9-5742-4c94-82eb-2f34884e3f4a,2025-09-01 05:54:00
|
||||
2025-09-01 13:49:14+08:00,2025-09-01 13:54:15+08:00,9.0,4396.35,4396.13,-1.980000000002292,平多,1743d43c-c642-434b-940f-b6d33e6718b9,e06fcad9-5742-4c94-82eb-2f34884e3f4a,2025-09-01 05:54:00
|
||||
2025-09-01 15:34:19+08:00,2025-09-01 15:35:08+08:00,403.0,4429.5,4426.01,-1406.469999999912,平多,e9dad14a-702d-4396-a259-38e6c3b62876,76c3e1c9-aeee-41bd-b8d4-37e8948af1c4,2025-09-01 07:35:00
|
||||
2025-09-01 15:34:19+08:00,2025-09-01 15:35:08+08:00,500.0,4429.5,4426.01,-1744.9999999998909,平多,431b7e4b-b81e-4626-a6de-f5acd5a7cf20,76c3e1c9-aeee-41bd-b8d4-37e8948af1c4,2025-09-01 07:35:00
|
||||
2025-09-01 15:36:02+08:00,2025-09-01 15:36:51+08:00,780.0,4424.4,4419.44,-3868.8000000000284,平多,d7ba8670-d0d7-4817-b30f-ed7d2ba4fd3c,74c07207-3acc-4f9c-b98b-ae54e4085d96,2025-09-01 07:36:00
|
||||
2025-09-01 15:36:02+08:00,2025-09-01 15:36:51+08:00,124.0,4424.4,4420.0,-545.5999999999549,平多,d7ba8670-d0d7-4817-b30f-ed7d2ba4fd3c,a4bea0b9-d2b0-42bf-9e3b-371747ea4fa3,2025-09-01 07:36:00
|
||||
2025-09-01 15:44:38+08:00,2025-09-01 15:47:21+08:00,676.0,4420.39,4426.94,4427.799999999508,平多,17564d45-1b57-4822-91dd-7bf3a162e7b1,131e056d-1779-4de8-bb12-1663936c3ffe,2025-09-01 07:47:00
|
||||
2025-09-01 15:44:38+08:00,2025-09-01 15:47:21+08:00,22.0,4420.0,4426.94,152.6799999999912,平多,7e280213-31c8-4848-b164-57ce0f3a30c3,131e056d-1779-4de8-bb12-1663936c3ffe,2025-09-01 07:47:00
|
||||
2025-09-01 15:44:38+08:00,2025-09-01 15:47:21+08:00,206.0,4420.39,4426.94,1349.2999999998501,平多,37898b4e-9a13-4bc0-a9e6-3fa908bd1e80,131e056d-1779-4de8-bb12-1663936c3ffe,2025-09-01 07:47:00
|
||||
2025-09-01 15:51:28+08:00,2025-09-01 15:54:46+08:00,387.0,4434.86,4450.54,6068.160000000113,平多,a62d6355-e666-4b24-a9fe-1a8efd802cd4,c5b380bf-78c7-4dfd-a1b8-f212e0562e5d,2025-09-01 07:54:00
|
||||
2025-09-01 15:51:28+08:00,2025-09-01 15:54:46+08:00,514.0,4434.86,4450.54,8059.52000000015,平多,9383482c-67d4-4a52-9c9a-bf106f8a66ed,c5b380bf-78c7-4dfd-a1b8-f212e0562e5d,2025-09-01 07:54:00
|
||||
2025-09-01 15:59:35+08:00,2025-09-01 16:02:01+08:00,898.0,4454.21,4472.89,16774.64000000026,平多,b4194b4d-5b4a-4387-879f-b208a173d23e,0b3a10ad-d3bc-49e7-bc76-95d3f3f7b012,2025-09-01 08:02:00
|
||||
2025-09-01 16:05:00+08:00,2025-09-01 16:05:43+08:00,891.0,4486.3,4479.29,-6245.9100000001945,平多,62035134-6e2c-4e78-a57e-90883d6fd125,e148f8b3-d2b0-49a8-9702-bf0ef6d70a11,2025-09-01 08:05:00
|
||||
2025-09-01 16:06:22+08:00,2025-09-01 16:13:08+08:00,893.0,4478.53,4480.64,-1884.2300000005198,平空,d901988b-b5b6-4a5e-a03c-83cedbdbbbda,dcc81b54-5253-4a49-a426-f28316341b68,2025-09-01 08:13:00
|
||||
2025-09-01 16:18:38+08:00,2025-09-01 16:19:03+08:00,895.0,4467.53,4470.68,-2819.2500000004884,平空,2a066536-51d7-425d-ae9e-83613597c612,41ad454f-5790-480c-9f16-80b478841763,2025-09-01 08:19:00
|
||||
2025-09-01 16:20:05+08:00,2025-09-01 16:22:22+08:00,894.0,4473.83,4478.12,-3835.2599999999675,平空,93df10c4-453d-405e-aa78-3870c5c5f78f,5aeedf17-a10d-4388-b16e-e18251d6e1e5,2025-09-01 08:22:00
|
||||
2025-09-01 16:23:14+08:00,2025-09-01 16:27:37+08:00,893.0,4475.22,4469.41,5188.330000000357,平空,d9277a5f-844a-4afc-b784-0343d373a87e,7def0a70-6cc3-4880-9da3-16671ec6a296,2025-09-01 08:27:00
|
||||
2025-09-01 16:33:42+08:00,2025-09-01 16:41:25+08:00,894.0,4470.45,4469.46,885.0599999998049,平空,9e7bf3df-d22c-413b-8b29-024b9c830f6b,3e80d0e3-3466-4e63-b75d-c86b67b6eb0b,2025-09-01 08:41:00
|
||||
2025-09-01 16:55:38+08:00,2025-09-01 17:04:46+08:00,893.0,4474.93,4476.7,-1580.6099999995777,平空,aca7bfae-9112-4a7a-8a95-7b7661be7d13,20115f47-253a-435a-9b43-1cfcbdc9c12e,2025-09-01 09:04:00
|
||||
2025-09-01 17:10:25+08:00,2025-09-01 17:15:02+08:00,894.0,4472.69,4480.01,6544.080000000553,平多,44c66b7e-5da3-4773-b5a0-73b1cd1c13b9,d8403012-f909-414d-bb21-a012dc5722c2,2025-09-01 09:15:00
|
||||
2025-09-01 17:35:29+08:00,2025-09-01 17:38:36+08:00,300.0,4473.01,4460.17,3852.0000000000437,平空,67981b9f-af13-4595-9a36-80db526ac0e4,de9b300a-19de-4cc4-b86e-e5d8ade4eb2f,2025-09-01 09:38:00
|
||||
2025-09-01 17:35:29+08:00,2025-09-01 17:38:36+08:00,70.0,4473.01,4460.17,898.8000000000102,平空,67981b9f-af13-4595-9a36-80db526ac0e4,f7f5c314-6e7b-498a-99cf-a2c868021b32,2025-09-01 09:38:00
|
||||
2025-09-01 17:35:29+08:00,2025-09-01 17:38:36+08:00,236.0,4473.01,4460.17,3030.2400000000343,平空,67981b9f-af13-4595-9a36-80db526ac0e4,f34ae213-3bb5-4bc5-96b5-1efb92648427,2025-09-01 09:38:00
|
||||
2025-09-01 17:35:29+08:00,2025-09-01 17:38:36+08:00,287.0,4473.01,4460.17,3685.0800000000418,平空,da23b541-1bde-4417-8875-4a52000d3183,f34ae213-3bb5-4bc5-96b5-1efb92648427,2025-09-01 09:38:00
|
||||
|
72
价格展示/test.py
72
价格展示/test.py
@@ -1,72 +0,0 @@
|
||||
import requests
|
||||
import pandas as pd
|
||||
|
||||
headers = {
|
||||
'accept': 'application/json, text/plain, */*',
|
||||
'accept-language': 'zh,zh-CN;q=0.9,zh-HK;q=0.8,en;q=0.7',
|
||||
'cache-control': 'no-cache',
|
||||
'origin': 'https://www.websea.com',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=1, i',
|
||||
'referer': 'https://www.websea.com/',
|
||||
'sec-ch-ua': '"Chromium";v="140", "Not=A?Brand";v="24", "Google Chrome";v="140"',
|
||||
'sec-ch-ua-mobile': '?0',
|
||||
'sec-ch-ua-platform': '"Windows"',
|
||||
'sec-fetch-dest': 'empty',
|
||||
'sec-fetch-mode': 'cors',
|
||||
'sec-fetch-site': 'same-site',
|
||||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36',
|
||||
}
|
||||
|
||||
if __name__ == '__main__':
|
||||
datas = []
|
||||
import datetime
|
||||
|
||||
# 定义开始日期和结束日期
|
||||
start_date = datetime.datetime(2025, 8, 28)
|
||||
end_date = datetime.datetime(2025, 9, 2)
|
||||
|
||||
# 初始化当前日期为开始日期
|
||||
current_date = start_date
|
||||
|
||||
# 循环遍历日期范围
|
||||
while current_date <= end_date:
|
||||
# 获取当天开始时刻(00:00:00)
|
||||
start_of_day = current_date.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
# 获取当天结束时刻(23:59:59)
|
||||
end_of_day = current_date.replace(hour=23, minute=59, second=59, microsecond=0)
|
||||
|
||||
# 将开始时刻和结束时刻转换为时间戳
|
||||
start_timestamp = start_of_day.timestamp()
|
||||
end_timestamp = end_of_day.timestamp()
|
||||
|
||||
print(f"日期: {current_date.strftime('%Y.%m.%d')}")
|
||||
print(f" 开始时刻时间戳: {start_timestamp}")
|
||||
print(f" 结束时刻时间戳: {end_timestamp}")
|
||||
|
||||
params = {
|
||||
'symbol': 'ETH-USDT',
|
||||
'period': '1min',
|
||||
'start': int(start_timestamp),
|
||||
'end': int(end_timestamp),
|
||||
}
|
||||
|
||||
response = requests.get('https://capi.websea.com/webApi/market/getKline', params=params, headers=headers)
|
||||
|
||||
# 提取数据
|
||||
data = response.json()['result']['data']
|
||||
print(data)
|
||||
|
||||
for i in data:
|
||||
datas.append(i)
|
||||
|
||||
# 日期加一天
|
||||
current_date += datetime.timedelta(days=1)
|
||||
|
||||
# 将数据转换为 DataFrame
|
||||
df = pd.DataFrame(datas)
|
||||
|
||||
# 保存为 Excel 文件
|
||||
df.to_excel('kline_data.xlsx', index=False)
|
||||
|
||||
print("数据已成功保存到 kline_data.xlsx 文件中。")
|
||||
712
价格展示/test1.py
712
价格展示/test1.py
@@ -1,712 +0,0 @@
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import plotly.graph_objects as go
|
||||
from datetime import datetime, timezone, timedelta
|
||||
import warnings
|
||||
import os
|
||||
import uuid
|
||||
|
||||
# ========== 配置 ==========
|
||||
KLINE_XLSX = "kline_data.xlsx" # K线数据文件名
|
||||
ORDERS_XLSX = "做市策略.xls" # 订单数据文件名
|
||||
OUTPUT_HTML = "kline_with_trades.html"
|
||||
SYMBOL = "ETH-USDT" # 交易对筛选
|
||||
|
||||
# 时间与对齐配置
|
||||
ORDERS_TIME_IS_LOCAL_ASIA_SH = True # 订单时间是否为东八区时间
|
||||
SNAP_TRADES_TO_NEAREST_CANDLE = True # 对齐交易点到最近的K线时间
|
||||
SNAP_TOLERANCE_MULTIPLIER = 1.5 # 对齐容忍度倍数
|
||||
|
||||
# 图表尺寸配置 - 更宽更扁
|
||||
CHART_WIDTH = 2200 # 更宽的图表
|
||||
CHART_HEIGHT = 600 # 更矮的图表
|
||||
FONT_SIZE = 12 # 字体大小
|
||||
ANNOTATION_FONT_SIZE = 10 # 标注字体大小
|
||||
MARKER_SIZE = 10 # 标记大小
|
||||
LINE_WIDTH = 1.5 # 连接线宽度
|
||||
|
||||
# 颜色配置 - 所有文本使用黑色
|
||||
TEXT_COLOR = "black" # 所有文本使用黑色
|
||||
TEXT_OFFSET = 10 # 文本偏移量(像素)
|
||||
|
||||
|
||||
# ========== 工具函数 ==========
|
||||
def parse_numeric(x):
|
||||
"""高效解析数值类型,支持多种格式"""
|
||||
if pd.isna(x):
|
||||
return np.nan
|
||||
try:
|
||||
# 尝试直接转换(大多数情况)
|
||||
return float(x)
|
||||
except:
|
||||
# 处理特殊格式
|
||||
s = str(x).replace(",", "").replace("USDT", "").replace("张", "").strip()
|
||||
if s.endswith("%"):
|
||||
s = s[:-1]
|
||||
return float(s) if s else np.nan
|
||||
|
||||
|
||||
def epoch_to_dt(x):
|
||||
"""将时间戳转换为上海时区时间"""
|
||||
try:
|
||||
return pd.to_datetime(int(x), unit="s", utc=True).tz_convert("Asia/Shanghai")
|
||||
except:
|
||||
return pd.NaT
|
||||
|
||||
|
||||
def zh_side(row):
|
||||
"""解析交易方向"""
|
||||
direction = str(row.get("方向", "")).strip()
|
||||
if "开多" in direction: return "long_open"
|
||||
if "平多" in direction: return "long_close"
|
||||
if "开空" in direction: return "short_open"
|
||||
if "平空" in direction: return "short_close"
|
||||
return "unknown"
|
||||
|
||||
|
||||
# ========== 数据加载与预处理 ==========
|
||||
def load_kline_data():
|
||||
"""加载并预处理K线数据"""
|
||||
if not os.path.exists(KLINE_XLSX):
|
||||
raise FileNotFoundError(f"K线数据文件不存在: {KLINE_XLSX}")
|
||||
|
||||
kdf = pd.read_excel(KLINE_XLSX, dtype=str)
|
||||
kdf.columns = [str(c).strip().lower() for c in kdf.columns]
|
||||
|
||||
# 验证必要列
|
||||
required_cols = {"id", "open", "close", "low", "high"}
|
||||
missing = required_cols - set(kdf.columns)
|
||||
if missing:
|
||||
raise ValueError(f"K线表缺少列: {missing}")
|
||||
|
||||
# 时间转换 - 确保id是秒级时间戳
|
||||
kdf["time"] = kdf["id"].apply(epoch_to_dt)
|
||||
|
||||
# 数值转换(向量化操作提升性能)
|
||||
for col in ["open", "close", "low", "high"]:
|
||||
kdf[col] = pd.to_numeric(kdf[col].apply(parse_numeric), errors="coerce")
|
||||
|
||||
# 清理无效数据
|
||||
kdf = kdf.dropna(subset=["time", "open", "close", "low", "high"])
|
||||
kdf = kdf.sort_values("time").reset_index(drop=True)
|
||||
|
||||
# 计算K线周期(用于交易点对齐)
|
||||
if len(kdf) >= 3:
|
||||
median_step = kdf["time"].diff().median()
|
||||
else:
|
||||
median_step = pd.Timedelta(minutes=1)
|
||||
|
||||
return kdf, median_step
|
||||
|
||||
|
||||
def load_order_data():
|
||||
"""加载并预处理订单数据"""
|
||||
if not os.path.exists(ORDERS_XLSX):
|
||||
raise FileNotFoundError(f"订单数据文件不存在: {ORDERS_XLSX}")
|
||||
|
||||
odf = pd.read_excel(ORDERS_XLSX, dtype=str)
|
||||
|
||||
# 验证必要列
|
||||
need_order_cols = ["时间", "交易对", "方向", "模式", "数量(张)", "成交价", "交易额", "消耗手续费", "用户盈亏"]
|
||||
missing = set(need_order_cols) - set(odf.columns)
|
||||
if missing:
|
||||
raise ValueError(f"订单表缺少列: {missing}")
|
||||
|
||||
# 筛选交易对
|
||||
if SYMBOL and "交易对" in odf.columns:
|
||||
odf = odf[odf["交易对"].astype(str).str.strip() == SYMBOL]
|
||||
|
||||
# 时间处理 - 确保时间格式正确
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
if ORDERS_TIME_IS_LOCAL_ASIA_SH:
|
||||
# 尝试多种格式解析时间
|
||||
odf["时间"] = pd.to_datetime(odf["时间"], errors="coerce", format="mixed")
|
||||
# 本地化为上海时区
|
||||
odf["时间"] = odf["时间"].dt.tz_localize("Asia/Shanghai", ambiguous="NaT", nonexistent="shift_forward")
|
||||
else:
|
||||
# 如果Excel时间已经是UTC
|
||||
odf["时间"] = pd.to_datetime(odf["时间"], utc=True, errors="coerce").dt.tz_convert("Asia/Shanghai")
|
||||
|
||||
# 数值转换
|
||||
numeric_cols = {
|
||||
"数量(张)": "数量",
|
||||
"成交价": "价格",
|
||||
"交易额": "交易额_num",
|
||||
"消耗手续费": "手续费",
|
||||
"用户盈亏": "盈亏"
|
||||
}
|
||||
|
||||
for src, dest in numeric_cols.items():
|
||||
odf[dest] = pd.to_numeric(odf[src].apply(parse_numeric), errors="coerce")
|
||||
|
||||
# 解析交易方向
|
||||
odf["side"] = odf.apply(zh_side, axis=1)
|
||||
|
||||
# 为每个订单生成唯一ID
|
||||
odf["order_id"] = [str(uuid.uuid4()) for _ in range(len(odf))]
|
||||
|
||||
# 计算本金(数量 * 价格)
|
||||
odf["本金"] = odf["数量"] * odf["价格"]
|
||||
|
||||
# 清理无效数据
|
||||
odf = odf.dropna(subset=["时间", "价格"])
|
||||
odf = odf.sort_values("时间").reset_index(drop=True)
|
||||
|
||||
return odf
|
||||
|
||||
|
||||
def align_trades_to_candles(kdf, odf, median_step):
|
||||
"""将交易点对齐到最近的K线时间"""
|
||||
if not SNAP_TRADES_TO_NEAREST_CANDLE or kdf.empty or odf.empty:
|
||||
return odf.assign(时间_x=odf["时间"])
|
||||
|
||||
snap_tolerance = pd.Timedelta(seconds=max(1, int(median_step.total_seconds() * SNAP_TOLERANCE_MULTIPLIER)))
|
||||
|
||||
# 使用merge_asof高效对齐
|
||||
anchor = kdf[["time"]].copy().rename(columns={"time": "k_time"})
|
||||
odf_sorted = odf.sort_values("时间")
|
||||
|
||||
aligned = pd.merge_asof(
|
||||
odf_sorted,
|
||||
anchor,
|
||||
left_on="时间",
|
||||
right_on="k_time",
|
||||
direction="nearest",
|
||||
tolerance=snap_tolerance
|
||||
)
|
||||
|
||||
# 保留原始时间作为参考
|
||||
aligned["原始时间"] = aligned["时间"]
|
||||
aligned["时间_x"] = aligned["k_time"].fillna(aligned["时间"])
|
||||
|
||||
return aligned
|
||||
|
||||
|
||||
# ========== 持仓跟踪与盈亏计算 ==========
|
||||
class PositionTracker:
|
||||
"""FIFO持仓跟踪器,支持订单走向可视化"""
|
||||
|
||||
def __init__(self):
|
||||
self.long_lots = [] # (数量, 价格, 时间, 手续费, 订单ID)
|
||||
self.short_lots = [] # (数量, 价格, 时间, 手续费, 订单ID)
|
||||
self.realized_pnl = 0.0
|
||||
self.history = [] # 记录所有交易历史
|
||||
self.trade_connections = [] # 记录开平仓连接关系
|
||||
|
||||
def open_long(self, qty, price, time, fee, order_id):
|
||||
"""开多仓"""
|
||||
if qty > 1e-9:
|
||||
self.long_lots.append((qty, price, time, fee, order_id))
|
||||
|
||||
def close_long(self, qty, price, time, fee, order_id):
|
||||
"""平多仓"""
|
||||
remaining = qty
|
||||
local_pnl = 0.0
|
||||
connections = [] # 本次平仓的连接关系
|
||||
|
||||
while remaining > 1e-9 and self.long_lots:
|
||||
lot_qty, lot_price, lot_time, lot_fee, open_order_id = self.long_lots[0]
|
||||
take = min(lot_qty, remaining)
|
||||
pnl = (price - lot_price) * take
|
||||
local_pnl += pnl
|
||||
lot_qty -= take
|
||||
remaining -= take
|
||||
|
||||
# 记录开平仓连接
|
||||
connection = {
|
||||
"open_time": lot_time,
|
||||
"close_time": time,
|
||||
"open_price": lot_price,
|
||||
"close_price": price,
|
||||
"qty": take,
|
||||
"pnl": pnl,
|
||||
"type": "long",
|
||||
"open_order_id": open_order_id,
|
||||
"close_order_id": order_id
|
||||
}
|
||||
self.trade_connections.append(connection)
|
||||
connections.append(connection)
|
||||
|
||||
# 记录平仓详情
|
||||
self.history.append({
|
||||
"开仓时间": lot_time,
|
||||
"平仓时间": time,
|
||||
"数量": take,
|
||||
"开仓价": lot_price,
|
||||
"平仓价": price,
|
||||
"盈亏": pnl,
|
||||
"类型": "平多",
|
||||
"开仓订单ID": open_order_id,
|
||||
"平仓订单ID": order_id
|
||||
})
|
||||
|
||||
if lot_qty <= 1e-9:
|
||||
self.long_lots.pop(0)
|
||||
else:
|
||||
self.long_lots[0] = (lot_qty, lot_price, lot_time, lot_fee, open_order_id)
|
||||
|
||||
local_pnl -= fee
|
||||
self.realized_pnl += local_pnl
|
||||
return local_pnl, connections
|
||||
|
||||
def open_short(self, qty, price, time, fee, order_id):
|
||||
"""开空仓"""
|
||||
if qty > 1e-9:
|
||||
self.short_lots.append((qty, price, time, fee, order_id))
|
||||
|
||||
def close_short(self, qty, price, time, fee, order_id):
|
||||
"""平空仓"""
|
||||
remaining = qty
|
||||
local_pnl = 0.0
|
||||
connections = [] # 本次平仓的连接关系
|
||||
|
||||
while remaining > 1e-9 and self.short_lots:
|
||||
lot_qty, lot_price, lot_time, lot_fee, open_order_id = self.short_lots[0]
|
||||
take = min(lot_qty, remaining)
|
||||
pnl = (lot_price - price) * take
|
||||
local_pnl += pnl
|
||||
lot_qty -= take
|
||||
remaining -= take
|
||||
|
||||
# 记录开平仓连接
|
||||
connection = {
|
||||
"open_time": lot_time,
|
||||
"close_time": time,
|
||||
"open_price": lot_price,
|
||||
"close_price": price,
|
||||
"qty": take,
|
||||
"pnl": pnl,
|
||||
"type": "short",
|
||||
"open_order_id": open_order_id,
|
||||
"close_order_id": order_id
|
||||
}
|
||||
self.trade_connections.append(connection)
|
||||
connections.append(connection)
|
||||
|
||||
# 记录平仓详情
|
||||
self.history.append({
|
||||
"开仓时间": lot_time,
|
||||
"平仓时间": time,
|
||||
"数量": take,
|
||||
"开仓价": lot_price,
|
||||
"平仓价": price,
|
||||
"盈亏": pnl,
|
||||
"类型": "平空",
|
||||
"开仓订单ID": open_order_id,
|
||||
"平仓订单ID": order_id
|
||||
})
|
||||
|
||||
if lot_qty <= 1e-9:
|
||||
self.short_lots.pop(0)
|
||||
else:
|
||||
self.short_lots[0] = (lot_qty, lot_price, lot_time, lot_fee, open_order_id)
|
||||
|
||||
local_pnl -= fee
|
||||
self.realized_pnl += local_pnl
|
||||
return local_pnl, connections
|
||||
|
||||
|
||||
def calculate_pnl(odf):
|
||||
"""计算持仓盈亏和订单连接关系"""
|
||||
tracker = PositionTracker()
|
||||
all_connections = []
|
||||
|
||||
for idx, r in odf.iterrows():
|
||||
qty = r["数量"]
|
||||
price = r["价格"]
|
||||
ts = r["时间"]
|
||||
fee = r["手续费"]
|
||||
side = r["side"]
|
||||
order_id = r["order_id"]
|
||||
|
||||
if side == "long_open":
|
||||
tracker.open_long(qty, price, ts, fee, order_id)
|
||||
elif side == "long_close":
|
||||
_, connections = tracker.close_long(qty, price, ts, fee, order_id)
|
||||
all_connections.extend(connections)
|
||||
elif side == "short_open":
|
||||
tracker.open_short(qty, price, ts, fee, order_id)
|
||||
elif side == "short_close":
|
||||
_, connections = tracker.close_short(qty, price, ts, fee, order_id)
|
||||
all_connections.extend(connections)
|
||||
|
||||
# 创建盈亏DataFrame
|
||||
if tracker.history:
|
||||
pnl_df = pd.DataFrame(tracker.history)
|
||||
# 添加对齐后的时间
|
||||
pnl_df["时间_x"] = pnl_df["平仓时间"].apply(
|
||||
lambda x: odf.loc[odf["时间"] == x, "时间_x"].values[0] if not odf.empty else x
|
||||
)
|
||||
else:
|
||||
pnl_df = pd.DataFrame()
|
||||
|
||||
# 创建连接关系DataFrame
|
||||
connections_df = pd.DataFrame(all_connections) if all_connections else pd.DataFrame()
|
||||
|
||||
return pnl_df, tracker.realized_pnl, connections_df
|
||||
|
||||
|
||||
# ========== 可视化 ==========
|
||||
def create_trade_scatter(df, name, color, symbol):
|
||||
"""创建交易点散点图"""
|
||||
if df.empty:
|
||||
return None
|
||||
|
||||
# 为不同类型的交易点创建不同的文本标签
|
||||
if name == "开多":
|
||||
text = "开多\n" + df["价格"].apply(lambda x: f"{x:.2f}") + "\n" + df["本金"].apply(lambda x: f"{x:.0f}")
|
||||
elif name == "平多":
|
||||
text = "平多\n" + df["价格"].apply(lambda x: f"{x:.2f}") + "\n" + df["盈亏"].apply(lambda x: f"{x:.0f}")
|
||||
elif name == "开空":
|
||||
text = "开空\n" + df["价格"].apply(lambda x: f"{x:.2f}") + "\n" + df["本金"].apply(lambda x: f"{x:.0f}")
|
||||
elif name == "平空":
|
||||
text = "平空\n" + df["价格"].apply(lambda x: f"{x:.2f}") + "\n" + df["盈亏"].apply(lambda x: f"{x:.0f}")
|
||||
else:
|
||||
text = name
|
||||
|
||||
return go.Scatter(
|
||||
x=df["时间_x"],
|
||||
y=df["价格"],
|
||||
mode="markers+text",
|
||||
name=name,
|
||||
text=text,
|
||||
textposition="middle right", # 文本放在右侧中间位置
|
||||
textfont=dict(size=ANNOTATION_FONT_SIZE, color=TEXT_COLOR), # 使用黑色文本
|
||||
marker=dict(
|
||||
size=MARKER_SIZE,
|
||||
color=color,
|
||||
symbol=symbol,
|
||||
line=dict(width=1.5, color="black")
|
||||
),
|
||||
customdata=np.stack([
|
||||
df["数量"].to_numpy(),
|
||||
df["价格"].to_numpy(),
|
||||
df["手续费"].to_numpy(),
|
||||
df.get("盈亏", np.nan).to_numpy(),
|
||||
df.get("原始时间", df["时间"]).dt.strftime("%Y-%m-%d %H:%M:%S").to_numpy(),
|
||||
df["order_id"].to_numpy(),
|
||||
df["本金"].to_numpy()
|
||||
], axis=-1),
|
||||
hovertemplate=(
|
||||
f"<b>{name}</b><br>"
|
||||
"数量: %{customdata[0]:.0f}张<br>"
|
||||
"价格: %{customdata[1]:.2f}<br>"
|
||||
"手续费: %{customdata[2]:.6f}<br>"
|
||||
"盈亏: %{customdata[3]:.4f}<br>"
|
||||
"本金: %{customdata[6]:.0f}<br>"
|
||||
"时间: %{customdata[4]}<br>"
|
||||
"订单ID: %{customdata[5]}<extra></extra>"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def add_trade_connections(fig, connections_df, odf):
|
||||
"""添加开平仓连接线"""
|
||||
if connections_df.empty:
|
||||
return
|
||||
|
||||
# 为盈利和亏损的连接线分别创建轨迹
|
||||
profit_lines = []
|
||||
loss_lines = []
|
||||
|
||||
for _, conn in connections_df.iterrows():
|
||||
# 获取开仓点和平仓点的坐标
|
||||
open_point = odf[odf["order_id"] == conn["open_order_id"]].iloc[0]
|
||||
close_point = odf[odf["order_id"] == conn["close_order_id"]].iloc[0]
|
||||
|
||||
line_data = {
|
||||
"x": [open_point["时间_x"], close_point["时间_x"]],
|
||||
"y": [open_point["价格"], close_point["价格"]],
|
||||
"pnl": conn["pnl"],
|
||||
"type": conn["type"],
|
||||
"open_order_id": conn["open_order_id"],
|
||||
"close_order_id": conn["close_order_id"]
|
||||
}
|
||||
|
||||
if conn["pnl"] >= 0:
|
||||
profit_lines.append(line_data)
|
||||
else:
|
||||
loss_lines.append(line_data)
|
||||
|
||||
# 添加盈利连接线(绿色)
|
||||
if profit_lines:
|
||||
x_profit = []
|
||||
y_profit = []
|
||||
customdata_profit = []
|
||||
|
||||
for line in profit_lines:
|
||||
x_profit.extend(line["x"])
|
||||
y_profit.extend(line["y"])
|
||||
x_profit.append(None)
|
||||
y_profit.append(None)
|
||||
|
||||
# 为每个点添加自定义数据
|
||||
customdata_profit.append([
|
||||
line["open_order_id"],
|
||||
line["close_order_id"],
|
||||
line["pnl"],
|
||||
line["type"]
|
||||
])
|
||||
customdata_profit.append([
|
||||
line["open_order_id"],
|
||||
line["close_order_id"],
|
||||
line["pnl"],
|
||||
line["type"]
|
||||
])
|
||||
customdata_profit.append(None)
|
||||
|
||||
fig.add_trace(go.Scatter(
|
||||
x=x_profit,
|
||||
y=y_profit,
|
||||
mode="lines",
|
||||
name="盈利订单",
|
||||
line=dict(color="rgba(46, 204, 113, 0.7)", width=LINE_WIDTH),
|
||||
hoverinfo="text",
|
||||
text=[f"盈利: {d[2]:.2f}" if d else None for d in customdata_profit],
|
||||
customdata=customdata_profit,
|
||||
hovertemplate=(
|
||||
"<b>%{text}</b><br>"
|
||||
"类型: %{customdata[3]}<br>"
|
||||
"开仓订单ID: %{customdata[0]}<br>"
|
||||
"平仓订单ID: %{customdata[1]}<extra></extra>"
|
||||
)
|
||||
))
|
||||
|
||||
# 添加亏损连接线(红色)
|
||||
if loss_lines:
|
||||
x_loss = []
|
||||
y_loss = []
|
||||
customdata_loss = []
|
||||
|
||||
for line in loss_lines:
|
||||
x_loss.extend(line["x"])
|
||||
y_loss.extend(line["y"])
|
||||
x_loss.append(None)
|
||||
y_loss.append(None)
|
||||
|
||||
# 为每个点添加自定义数据
|
||||
customdata_loss.append([
|
||||
line["open_order_id"],
|
||||
line["close_order_id"],
|
||||
line["pnl"],
|
||||
line["type"]
|
||||
])
|
||||
customdata_loss.append([
|
||||
line["open_order_id"],
|
||||
line["close_order_id"],
|
||||
line["pnl"],
|
||||
line["type"]
|
||||
])
|
||||
customdata_loss.append(None)
|
||||
|
||||
fig.add_trace(go.Scatter(
|
||||
x=x_loss,
|
||||
y=y_loss,
|
||||
mode="lines",
|
||||
name="亏损订单",
|
||||
line=dict(color="rgba(231, 76, 60, 0.7)", width=LINE_WIDTH),
|
||||
hoverinfo="text",
|
||||
text=[f"亏损: {abs(d[2]):.2f}" if d else None for d in customdata_loss],
|
||||
customdata=customdata_loss,
|
||||
hovertemplate=(
|
||||
"<b>%{text}</b><br>"
|
||||
"类型: %{customdata[3]}<br>"
|
||||
"开仓订单ID: %{customdata[0]}<br>"
|
||||
"平仓订单ID: %{customdata[1]}<extra></extra>"
|
||||
)
|
||||
))
|
||||
|
||||
|
||||
def generate_chart(kdf, odf, pnl_df, cum_realized, connections_df):
|
||||
"""生成K线图与交易标注"""
|
||||
fig = go.Figure()
|
||||
|
||||
# K线主图
|
||||
fig.add_trace(go.Candlestick(
|
||||
x=kdf["time"],
|
||||
open=kdf["open"],
|
||||
high=kdf["high"],
|
||||
low=kdf["low"],
|
||||
close=kdf["close"],
|
||||
name="K线",
|
||||
increasing_line_color="#2ecc71",
|
||||
decreasing_line_color="#e74c3c"
|
||||
))
|
||||
|
||||
# 添加交易点
|
||||
trade_types = [
|
||||
(odf[odf["side"] == "long_open"], "开多", "#2ecc71", "triangle-up"),
|
||||
(odf[odf["side"] == "long_close"], "平多", "#27ae60", "circle"),
|
||||
(odf[odf["side"] == "short_open"], "开空", "#e74c3c", "triangle-down"),
|
||||
(odf[odf["side"] == "short_close"], "平空", "#c0392b", "x")
|
||||
]
|
||||
|
||||
for data, name, color, symbol in trade_types:
|
||||
trace = create_trade_scatter(data, name, color, symbol)
|
||||
if trace:
|
||||
fig.add_trace(trace)
|
||||
|
||||
# 添加开平仓连接线
|
||||
add_trade_connections(fig, connections_df, odf)
|
||||
|
||||
# 计算时间范围,确保所有点都显示在图表中
|
||||
all_times = pd.concat([kdf["time"], odf["时间_x"]])
|
||||
min_time = all_times.min() - pd.Timedelta(minutes=10)
|
||||
max_time = all_times.max() + pd.Timedelta(minutes=10)
|
||||
|
||||
# 计算价格范围,确保所有点都显示在图表中
|
||||
min_price = min(kdf["low"].min(), odf["价格"].min()) * 0.99
|
||||
max_price = max(kdf["high"].max(), odf["价格"].max()) * 1.01
|
||||
|
||||
# 布局配置 - 更宽更扁的图表
|
||||
fig.update_layout(
|
||||
xaxis_title="时间",
|
||||
yaxis_title="价格 (USDT)",
|
||||
legend=dict(
|
||||
orientation="h",
|
||||
yanchor="bottom",
|
||||
y=1.02,
|
||||
xanchor="left",
|
||||
x=0,
|
||||
font=dict(size=FONT_SIZE, color=TEXT_COLOR) # 图例文字使用黑色
|
||||
),
|
||||
xaxis=dict(
|
||||
rangeslider=dict(visible=False),
|
||||
type="date",
|
||||
gridcolor="rgba(128, 128, 128, 0.2)",
|
||||
range=[min_time, max_time], # 设置时间范围
|
||||
title_font=dict(color=TEXT_COLOR), # 坐标轴标题使用黑色
|
||||
tickfont=dict(color=TEXT_COLOR) # 刻度标签使用黑色
|
||||
),
|
||||
yaxis=dict(
|
||||
gridcolor="rgba(128, 128, 128, 0.2)",
|
||||
range=[min_price, max_price], # 设置价格范围
|
||||
title_font=dict(color=TEXT_COLOR), # 坐标轴标题使用黑色
|
||||
tickfont=dict(color=TEXT_COLOR) # 刻度标签使用黑色
|
||||
),
|
||||
hovermode="x unified",
|
||||
hoverlabel=dict(
|
||||
namelength=-1,
|
||||
bgcolor="rgba(255, 255, 255, 0.9)",
|
||||
font_size=FONT_SIZE,
|
||||
font_color=TEXT_COLOR # 悬停标签文字使用黑色
|
||||
),
|
||||
margin=dict(l=50, r=50, t=80, b=50),
|
||||
plot_bgcolor="rgba(240, 240, 240, 1)",
|
||||
width=CHART_WIDTH, # 使用配置的宽度
|
||||
height=CHART_HEIGHT, # 使用配置的高度
|
||||
font=dict(size=FONT_SIZE, color=TEXT_COLOR), # 全局字体大小和颜色
|
||||
|
||||
# 增强交互性配置
|
||||
dragmode="pan", # 默认拖拽模式为平移
|
||||
clickmode="event+select", # 点击模式
|
||||
selectdirection="h", # 水平选择方向
|
||||
modebar=dict(
|
||||
orientation="h", # 水平方向工具栏
|
||||
bgcolor="rgba(255, 255, 255, 0.7)", # 半透明背景
|
||||
color="rgba(0, 0, 0, 0.7)", # 图标颜色
|
||||
activecolor="rgba(0, 0, 0, 0.9)" # 激活图标颜色
|
||||
)
|
||||
)
|
||||
|
||||
# 添加模式栏按钮
|
||||
fig.update_layout(
|
||||
modebar_add=[
|
||||
"zoom2d",
|
||||
"pan2d",
|
||||
"select2d",
|
||||
"lasso2d",
|
||||
"zoomIn2d",
|
||||
"zoomOut2d",
|
||||
"autoScale2d",
|
||||
"resetScale2d",
|
||||
"toImage"
|
||||
]
|
||||
)
|
||||
|
||||
# 配置缩放行为 - 确保滚轮缩放正常工作
|
||||
fig.update_xaxes(
|
||||
autorange=False,
|
||||
fixedrange=False, # 允许缩放
|
||||
constrain="domain", # 约束在域内
|
||||
rangeslider=dict(visible=False) # 禁用范围滑块
|
||||
)
|
||||
|
||||
fig.update_yaxes(
|
||||
autorange=False,
|
||||
fixedrange=False, # 允许缩放
|
||||
scaleanchor="x", # 保持纵横比
|
||||
scaleratio=1, # 缩放比例
|
||||
constrain="domain" # 约束在域内
|
||||
)
|
||||
|
||||
# 保存并打开结果 - 启用滚轮缩放
|
||||
fig.write_html(
|
||||
OUTPUT_HTML,
|
||||
include_plotlyjs="cdn",
|
||||
auto_open=True,
|
||||
config={
|
||||
'scrollZoom': True, # 启用滚轮缩放
|
||||
'displayModeBar': True, # 显示工具栏
|
||||
'displaylogo': False, # 隐藏Plotly标志
|
||||
'responsive': True # 响应式布局
|
||||
}
|
||||
)
|
||||
print(f"图表已生成: {OUTPUT_HTML}")
|
||||
|
||||
# 返回盈亏详情
|
||||
if not pnl_df.empty:
|
||||
pnl_df.to_csv("pnl_details.csv", index=False)
|
||||
print(f"盈亏详情已保存: pnl_details.csv")
|
||||
|
||||
if not connections_df.empty:
|
||||
connections_df.to_csv("trade_connections.csv", index=False)
|
||||
print(f"订单连接关系已保存: trade_connections.csv")
|
||||
|
||||
return fig
|
||||
|
||||
|
||||
# ========== 主执行流程 ==========
|
||||
def main():
|
||||
print("开始处理数据...")
|
||||
|
||||
# 加载数据
|
||||
kdf, median_step = load_kline_data()
|
||||
odf = load_order_data()
|
||||
|
||||
print(f"加载K线数据: {len(kdf)}条")
|
||||
print(f"加载订单数据: {len(odf)}条")
|
||||
|
||||
# 对齐交易时间
|
||||
odf = align_trades_to_candles(kdf, odf, median_step)
|
||||
|
||||
# 检查时间范围
|
||||
kline_min_time = kdf["time"].min()
|
||||
kline_max_time = kdf["time"].max()
|
||||
order_min_time = odf["时间"].min()
|
||||
order_max_time = odf["时间"].max()
|
||||
|
||||
print(f"K线时间范围: {kline_min_time} 至 {kline_max_time}")
|
||||
print(f"订单时间范围: {order_min_time} 至 {order_max_time}")
|
||||
|
||||
# 检查是否有订单在K线时间范围外
|
||||
outside_orders = odf[(odf["时间"] < kline_min_time) | (odf["时间"] > kline_max_time)]
|
||||
if not outside_orders.empty:
|
||||
print(f"警告: 有 {len(outside_orders)} 个订单在K线时间范围外")
|
||||
print(outside_orders[["时间", "方向", "价格"]])
|
||||
|
||||
# 计算盈亏和订单连接关系
|
||||
pnl_df, cum_realized, connections_df = calculate_pnl(odf)
|
||||
print(f"累计已实现盈亏: {cum_realized:.2f} USDT")
|
||||
print(f"订单连接关系: {len(connections_df)}条")
|
||||
|
||||
# 生成图表
|
||||
generate_chart(kdf, odf, pnl_df, cum_realized, connections_df)
|
||||
print("处理完成")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
main()
|
||||
713
价格展示/test2.py
713
价格展示/test2.py
@@ -1,713 +0,0 @@
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import plotly.graph_objects as go
|
||||
from datetime import datetime, timezone, timedelta
|
||||
import warnings
|
||||
import os
|
||||
import uuid
|
||||
|
||||
# ========== 配置 ==========
|
||||
KLINE_XLSX = "kline_data.xlsx" # K线数据文件名
|
||||
ORDERS_XLSX = "做市策略.xls" # 订单数据文件名
|
||||
OUTPUT_HTML = "kline_with_trades.html"
|
||||
SYMBOL = "ETH-USDT" # 交易对筛选
|
||||
|
||||
# 时间与对齐配置
|
||||
ORDERS_TIME_IS_LOCAL_ASIA_SH = True # 订单时间是否为东八区时间
|
||||
SNAP_TRADES_TO_NEAREST_CANDLE = True # 对齐交易点到最近的K线时间
|
||||
SNAP_TOLERANCE_MULTIPLIER = 1.5 # 对齐容忍度倍数
|
||||
|
||||
# 图表尺寸配置 - 更宽更扁
|
||||
CHART_WIDTH = 2200 # 更宽的图表
|
||||
CHART_HEIGHT = 600 # 更矮的图表
|
||||
FONT_SIZE = 12 # 字体大小
|
||||
ANNOTATION_FONT_SIZE = 10 # 标注字体大小
|
||||
MARKER_SIZE = 10 # 标记大小
|
||||
LINE_WIDTH = 1.5 # 连接线宽度
|
||||
|
||||
# 颜色配置 - 所有文本使用黑色
|
||||
TEXT_COLOR = "black" # 所有文本使用黑色
|
||||
TEXT_OFFSET = 10 # 文本偏移量(像素)
|
||||
|
||||
|
||||
# ========== 工具函数 ==========
|
||||
def parse_numeric(x):
|
||||
"""高效解析数值类型,支持多种格式"""
|
||||
if pd.isna(x):
|
||||
return np.nan
|
||||
try:
|
||||
# 尝试直接转换(大多数情况)
|
||||
return float(x)
|
||||
except:
|
||||
# 处理特殊格式
|
||||
s = str(x).replace(",", "").replace("USDT", "").replace("张", "").strip()
|
||||
if s.endswith("%"):
|
||||
s = s[:-1]
|
||||
return float(s) if s else np.nan
|
||||
|
||||
|
||||
def epoch_to_dt(x):
|
||||
"""将时间戳转换为上海时区时间"""
|
||||
try:
|
||||
return pd.to_datetime(int(x), unit="s", utc=True).tz_convert("Asia/Shanghai")
|
||||
except:
|
||||
return pd.NaT
|
||||
|
||||
|
||||
def zh_side(row):
|
||||
"""解析交易方向"""
|
||||
direction = str(row.get("方向", "")).strip()
|
||||
if "开多" in direction: return "long_open"
|
||||
if "平多" in direction: return "long_close"
|
||||
if "开空" in direction: return "short_open"
|
||||
if "平空" in direction: return "short_close"
|
||||
return "unknown"
|
||||
|
||||
|
||||
# ========== 数据加载与预处理 ==========
|
||||
def load_kline_data():
|
||||
"""加载并预处理K线数据"""
|
||||
if not os.path.exists(KLINE_XLSX):
|
||||
raise FileNotFoundError(f"K线数据文件不存在: {KLINE_XLSX}")
|
||||
|
||||
kdf = pd.read_excel(KLINE_XLSX, dtype=str)
|
||||
kdf.columns = [str(c).strip().lower() for c in kdf.columns]
|
||||
|
||||
# 验证必要列
|
||||
required_cols = {"id", "open", "close", "low", "high"}
|
||||
missing = required_cols - set(kdf.columns)
|
||||
if missing:
|
||||
raise ValueError(f"K线表缺少列: {missing}")
|
||||
|
||||
# 时间转换 - 确保id是秒级时间戳
|
||||
kdf["time"] = kdf["id"].apply(epoch_to_dt)
|
||||
|
||||
# 数值转换(向量化操作提升性能)
|
||||
for col in ["open", "close", "low", "high"]:
|
||||
kdf[col] = pd.to_numeric(kdf[col].apply(parse_numeric), errors="coerce")
|
||||
|
||||
# 清理无效数据
|
||||
kdf = kdf.dropna(subset=["time", "open", "close", "low", "high"])
|
||||
kdf = kdf.sort_values("time").reset_index(drop=True)
|
||||
|
||||
# 计算K线周期(用于交易点对齐)
|
||||
if len(kdf) >= 3:
|
||||
median_step = kdf["time"].diff().median()
|
||||
else:
|
||||
median_step = pd.Timedelta(minutes=1)
|
||||
|
||||
return kdf, median_step
|
||||
|
||||
|
||||
def load_order_data():
|
||||
"""加载并预处理订单数据"""
|
||||
if not os.path.exists(ORDERS_XLSX):
|
||||
raise FileNotFoundError(f"订单数据文件不存在: {ORDERS_XLSX}")
|
||||
|
||||
odf = pd.read_excel(ORDERS_XLSX, dtype=str)
|
||||
|
||||
# 验证必要列
|
||||
need_order_cols = ["时间", "交易对", "方向", "模式", "数量(张)", "成交价", "交易额", "消耗手续费", "用户盈亏"]
|
||||
missing = set(need_order_cols) - set(odf.columns)
|
||||
if missing:
|
||||
raise ValueError(f"订单表缺少列: {missing}")
|
||||
|
||||
# 筛选交易对
|
||||
if SYMBOL and "交易对" in odf.columns:
|
||||
odf = odf[odf["交易对"].astype(str).str.strip() == SYMBOL]
|
||||
|
||||
# 时间处理 - 确保时间格式正确
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
if ORDERS_TIME_IS_LOCAL_ASIA_SH:
|
||||
# 尝试多种格式解析时间
|
||||
odf["时间"] = pd.to_datetime(odf["时间"], errors="coerce", format="mixed")
|
||||
# 本地化为上海时区
|
||||
odf["时间"] = odf["时间"].dt.tz_localize("Asia/Shanghai", ambiguous="NaT", nonexistent="shift_forward")
|
||||
else:
|
||||
# 如果Excel时间已经是UTC
|
||||
odf["时间"] = pd.to_datetime(odf["时间"], utc=True, errors="coerce").dt.tz_convert("Asia/Shanghai")
|
||||
|
||||
# 数值转换
|
||||
numeric_cols = {
|
||||
"数量(张)": "数量",
|
||||
"成交价": "价格",
|
||||
"交易额": "交易额_num",
|
||||
"消耗手续费": "手续费",
|
||||
"用户盈亏": "盈亏"
|
||||
}
|
||||
|
||||
for src, dest in numeric_cols.items():
|
||||
odf[dest] = pd.to_numeric(odf[src].apply(parse_numeric), errors="coerce")
|
||||
|
||||
# 解析交易方向
|
||||
odf["side"] = odf.apply(zh_side, axis=1)
|
||||
|
||||
# 为每个订单生成唯一ID
|
||||
odf["order_id"] = [str(uuid.uuid4()) for _ in range(len(odf))]
|
||||
|
||||
# 计算本金(数量 * 价格)
|
||||
odf["本金"] = odf["数量"] * odf["价格"]
|
||||
|
||||
# 清理无效数据
|
||||
odf = odf.dropna(subset=["时间", "价格"])
|
||||
odf = odf.sort_values("时间").reset_index(drop=True)
|
||||
|
||||
return odf
|
||||
|
||||
|
||||
def align_trades_to_candles(kdf, odf, median_step):
|
||||
"""将交易点对齐到最近的K线时间"""
|
||||
if not SNAP_TRADES_TO_NEAREST_CANDLE or kdf.empty or odf.empty:
|
||||
return odf.assign(时间_x=odf["时间"])
|
||||
|
||||
snap_tolerance = pd.Timedelta(seconds=max(1, int(median_step.total_seconds() * SNAP_TOLERANCE_MULTIPLIER)))
|
||||
|
||||
# 使用merge_asof高效对齐 - 使用方向为'backward'确保交易点对齐到前一个K线
|
||||
anchor = kdf[["time"]].copy().rename(columns={"time": "k_time"})
|
||||
odf_sorted = odf.sort_values("时间")
|
||||
|
||||
# 关键优化:使用'backward'方向确保交易点对齐到前一个K线
|
||||
aligned = pd.merge_asof(
|
||||
odf_sorted,
|
||||
anchor,
|
||||
left_on="时间",
|
||||
right_on="k_time",
|
||||
direction="backward", # 使用'backward'确保交易点对齐到前一个K线
|
||||
tolerance=snap_tolerance
|
||||
)
|
||||
|
||||
# 保留原始时间作为参考
|
||||
aligned["原始时间"] = aligned["时间"]
|
||||
aligned["时间_x"] = aligned["k_time"].fillna(aligned["时间"])
|
||||
|
||||
return aligned
|
||||
|
||||
|
||||
# ========== 持仓跟踪与盈亏计算 ==========
|
||||
class PositionTracker:
|
||||
"""FIFO持仓跟踪器,支持订单走向可视化"""
|
||||
|
||||
def __init__(self):
|
||||
self.long_lots = [] # (数量, 价格, 时间, 手续费, 订单ID)
|
||||
self.short_lots = [] # (数量, 价格, 时间, 手续费, 订单ID)
|
||||
self.realized_pnl = 0.0
|
||||
self.history = [] # 记录所有交易历史
|
||||
self.trade_connections = [] # 记录开平仓连接关系
|
||||
|
||||
def open_long(self, qty, price, time, fee, order_id):
|
||||
"""开多仓"""
|
||||
if qty > 1e-9:
|
||||
self.long_lots.append((qty, price, time, fee, order_id))
|
||||
|
||||
def close_long(self, qty, price, time, fee, order_id):
|
||||
"""平多仓"""
|
||||
remaining = qty
|
||||
local_pnl = 0.0
|
||||
connections = [] # 本次平仓的连接关系
|
||||
|
||||
while remaining > 1e-9 and self.long_lots:
|
||||
lot_qty, lot_price, lot_time, lot_fee, open_order_id = self.long_lots[0]
|
||||
take = min(lot_qty, remaining)
|
||||
pnl = (price - lot_price) * take
|
||||
local_pnl += pnl
|
||||
lot_qty -= take
|
||||
remaining -= take
|
||||
|
||||
# 记录开平仓连接
|
||||
connection = {
|
||||
"open_time": lot_time,
|
||||
"close_time": time,
|
||||
"open_price": lot_price,
|
||||
"close_price": price,
|
||||
"qty": take,
|
||||
"pnl": pnl,
|
||||
"type": "long",
|
||||
"open_order_id": open_order_id,
|
||||
"close_order_id": order_id
|
||||
}
|
||||
self.trade_connections.append(connection)
|
||||
connections.append(connection)
|
||||
|
||||
# 记录平仓详情
|
||||
self.history.append({
|
||||
"开仓时间": lot_time,
|
||||
"平仓时间": time,
|
||||
"数量": take,
|
||||
"开仓价": lot_price,
|
||||
"平仓价": price,
|
||||
"盈亏": pnl,
|
||||
"类型": "平多",
|
||||
"开仓订单ID": open_order_id,
|
||||
"平仓订单ID": order_id
|
||||
})
|
||||
|
||||
if lot_qty <= 1e-9:
|
||||
self.long_lots.pop(0)
|
||||
else:
|
||||
self.long_lots[0] = (lot_qty, lot_price, lot_time, lot_fee, open_order_id)
|
||||
|
||||
local_pnl -= fee
|
||||
self.realized_pnl += local_pnl
|
||||
return local_pnl, connections
|
||||
|
||||
def open_short(self, qty, price, time, fee, order_id):
|
||||
"""开空仓"""
|
||||
if qty > 1e-9:
|
||||
self.short_lots.append((qty, price, time, fee, order_id))
|
||||
|
||||
def close_short(self, qty, price, time, fee, order_id):
|
||||
"""平空仓"""
|
||||
remaining = qty
|
||||
local_pnl = 0.0
|
||||
connections = [] # 本次平仓的连接关系
|
||||
|
||||
while remaining > 1e-9 and self.short_lots:
|
||||
lot_qty, lot_price, lot_time, lot_fee, open_order_id = self.short_lots[0]
|
||||
take = min(lot_qty, remaining)
|
||||
pnl = (lot_price - price) * take
|
||||
local_pnl += pnl
|
||||
lot_qty -= take
|
||||
remaining -= take
|
||||
|
||||
# 记录开平仓连接
|
||||
connection = {
|
||||
"open_time": lot_time,
|
||||
"close_time": time,
|
||||
"open_price": lot_price,
|
||||
"close_price": price,
|
||||
"q极": take,
|
||||
"pnl": pnl,
|
||||
"type": "short",
|
||||
"open_order_id": open_order_id,
|
||||
"close_order_id": order_id
|
||||
}
|
||||
self.trade_connections.append(connection)
|
||||
connections.append(connection)
|
||||
|
||||
# 记录平仓详情
|
||||
self.history.append({
|
||||
"开仓时间": lot_time,
|
||||
"平仓时间": time,
|
||||
"数量": take,
|
||||
"开仓价": lot_price,
|
||||
"平仓价": price,
|
||||
"盈亏": pnl,
|
||||
"类型": "平空",
|
||||
"开仓订单ID": open_order_id,
|
||||
"平仓订单ID": order_id
|
||||
})
|
||||
|
||||
if lot_qty <= 1e-9:
|
||||
self.short_lots.pop(0)
|
||||
else:
|
||||
self.short_lots[0] = (lot_qty, lot_price, lot_time, lot_fee, open_order_id)
|
||||
|
||||
local_pnl -= fee
|
||||
self.realized_pnl += local_pnl
|
||||
return local_pnl, connections
|
||||
|
||||
|
||||
def calculate_pnl(odf):
|
||||
"""计算持仓盈亏和订单连接关系"""
|
||||
tracker = PositionTracker()
|
||||
all_connections = []
|
||||
|
||||
for idx, r in odf.iterrows():
|
||||
qty = r["数量"]
|
||||
price = r["价格"]
|
||||
ts = r["时间"]
|
||||
fee = r["手续费"]
|
||||
side = r["side"]
|
||||
order_id = r["order_id"]
|
||||
|
||||
if side == "long_open":
|
||||
tracker.open_long(qty, price, ts, fee, order_id)
|
||||
elif side == "long_close":
|
||||
_, connections = tracker.close_long(qty, price, ts, fee, order_id)
|
||||
all_connections.extend(connections)
|
||||
elif side == "short_open":
|
||||
tracker.open_short(qty, price, ts, fee, order_id)
|
||||
elif side == "short_close":
|
||||
_, connections = tracker.close_short(qty, price, ts, fee, order_id)
|
||||
all_connections.extend(connections)
|
||||
|
||||
# 创建盈亏DataFrame
|
||||
if tracker.history:
|
||||
pnl_df = pd.DataFrame(tracker.history)
|
||||
# 添加对齐后的时间
|
||||
pnl_df["时间_x"] = pnl_df["平仓时间"].apply(
|
||||
lambda x: odf.loc[odf["时间"] == x, "时间_x"].values[0] if not odf.empty else x
|
||||
)
|
||||
else:
|
||||
pnl_df = pd.DataFrame()
|
||||
|
||||
# 创建连接关系DataFrame
|
||||
connections_df = pd.DataFrame(all_connections) if all_connections else pd.DataFrame()
|
||||
|
||||
return pnl_df, tracker.realized_pnl, connections_df
|
||||
|
||||
|
||||
# ========== 可视化 ==========
|
||||
def create_trade_scatter(df, name, color, symbol):
|
||||
"""创建交易点散点图"""
|
||||
if df.empty:
|
||||
return None
|
||||
|
||||
# 为不同类型的交易点创建不同的文本标签
|
||||
if name == "开多":
|
||||
text = "开多\n" + df["价格"].apply(lambda x: f"{x:.2f}") + "\n" + df["本金"].apply(lambda x: f"{x:.0f}")
|
||||
elif name == "平多":
|
||||
text = "平多\n" + df["价格"].apply(lambda x: f"{x:.2f}") + "\n" + df["盈亏"].apply(lambda x: f"{x:.0f}")
|
||||
elif name == "开空":
|
||||
text = "开空\n" + df["价格"].apply(lambda x: f"{x:.2f}") + "\n" + df["本金"].apply(lambda x: f"{x:.0f}")
|
||||
elif name == "平空":
|
||||
text = "平空\n" + df["价格"].apply(lambda x: f"{x:.2f}") + "\n" + df["盈亏"].apply(lambda x: f"{x:.0f}")
|
||||
else:
|
||||
text = name
|
||||
|
||||
return go.Scatter(
|
||||
x=df["时间_x"],
|
||||
y=df["价格"],
|
||||
mode="markers+text",
|
||||
name=name,
|
||||
text=text,
|
||||
textposition="middle right", # 文本放在右侧中间位置
|
||||
textfont=dict(size=ANNOTATION_FONT_SIZE, color=TEXT_COLOR), # 使用黑色文本
|
||||
marker=dict(
|
||||
size=MARKER_SIZE,
|
||||
color=color,
|
||||
symbol=symbol,
|
||||
line=dict(width=1.5, color="black")
|
||||
),
|
||||
customdata=np.stack([
|
||||
df["数量"].to_numpy(),
|
||||
df["价格"].to_numpy(),
|
||||
df["手续费"].to_numpy(),
|
||||
df.get("盈亏", np.nan).to_numpy(),
|
||||
df.get("原始时间", df["时间"]).dt.strftime("%Y-%m-%d %H:%M:%S").to_numpy(),
|
||||
df["order_id"].to_numpy(),
|
||||
df["本金"].to_numpy()
|
||||
], axis=-1),
|
||||
hovertemplate=(
|
||||
f"<b>{name}</b><br>"
|
||||
"数量: %{customdata[0]:.0f}张<br>"
|
||||
"价格: %{customdata[1]:.2f}<br>"
|
||||
"手续费: %{customdata[2]:.6f}<br>"
|
||||
"盈亏: %{customdata[3]:.4f}<br>"
|
||||
"本金: %{customdata[6]:.0f}<br>"
|
||||
"时间: %{customdata[4]}<br>"
|
||||
"订单ID: %{customdata[5]}<extra></extra>"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def add_trade_connections(fig, connections_df, odf):
|
||||
"""添加开平仓连接线"""
|
||||
if connections_df.empty:
|
||||
return
|
||||
|
||||
# 为盈利和亏损的连接线分别创建轨迹
|
||||
profit_lines = []
|
||||
loss_lines = []
|
||||
|
||||
for _, conn in connections_df.iterrows():
|
||||
# 获取开仓点和平仓点的坐标
|
||||
open_point = odf[odf["order_id"] == conn["open_order_id"]].iloc[0]
|
||||
close_point = odf[odf["order_id"] == conn["close_order_id"]].iloc[0]
|
||||
|
||||
line_data = {
|
||||
"x": [open_point["时间_x"], close_point["时间_x"]],
|
||||
"y": [open_point["价格"], close_point["价格"]],
|
||||
"pnl": conn["pnl"],
|
||||
"type": conn["type"],
|
||||
"open_order_id": conn["open_order_id"],
|
||||
"close_order_id": conn["close_order_id"]
|
||||
}
|
||||
|
||||
if conn["pnl"] >= 0:
|
||||
profit_lines.append(line_data)
|
||||
else:
|
||||
loss_lines.append(line_data)
|
||||
|
||||
# 添加盈利连接线(绿色)
|
||||
if profit_lines:
|
||||
x_profit = []
|
||||
y_profit = []
|
||||
customdata_profit = []
|
||||
|
||||
for line in profit_lines:
|
||||
x_profit.extend(line["x"])
|
||||
y_profit.extend(line["y"])
|
||||
x_profit.append(None)
|
||||
y_profit.append(None)
|
||||
|
||||
# 为每个点添加自定义数据
|
||||
customdata_profit.append([
|
||||
line["open_order_id"],
|
||||
line["close_order_id"],
|
||||
line["pnl"],
|
||||
line["type"]
|
||||
])
|
||||
customdata_profit.append([
|
||||
line["open_order_id"],
|
||||
line["close_order_id"],
|
||||
line["pnl"],
|
||||
line["type"]
|
||||
])
|
||||
customdata_profit.append(None)
|
||||
|
||||
fig.add_trace(go.Scatter(
|
||||
x=x_profit,
|
||||
y=y_profit,
|
||||
mode="lines",
|
||||
name="盈利订单",
|
||||
line=dict(color="rgba(46, 204, 113, 0.7)", width=LINE_WIDTH),
|
||||
hoverinfo="text",
|
||||
text=[f"盈利: {d[2]:.2f}" if d else None for d in customdata_profit],
|
||||
customdata=customdata_profit,
|
||||
hovertemplate=(
|
||||
"<b>%{text}</b><br>"
|
||||
"类型: %{customdata[3]}<br>"
|
||||
"开仓订单ID: %{customdata[0]}<br>"
|
||||
"平仓订单ID: %{customdata[1]}<extra></extra>"
|
||||
)
|
||||
))
|
||||
|
||||
# 添加亏损连接线(红色)
|
||||
if loss_lines:
|
||||
x_loss = []
|
||||
y_loss = []
|
||||
customdata_loss = []
|
||||
|
||||
for line in loss_lines:
|
||||
x_loss.extend(line["x"])
|
||||
y_loss.extend(line["y"])
|
||||
x_loss.append(None)
|
||||
y_loss.append(None)
|
||||
|
||||
# 为每个点添加自定义数据
|
||||
customdata_loss.append([
|
||||
line["open_order_id"],
|
||||
line["close_order_id"],
|
||||
line["pnl"],
|
||||
line["type"]
|
||||
])
|
||||
customdata_loss.append([
|
||||
line["open_order_id"],
|
||||
line["close_order_id"],
|
||||
line["pnl"],
|
||||
line["type"]
|
||||
])
|
||||
customdata_loss.append(None)
|
||||
|
||||
fig.add_trace(go.Scatter(
|
||||
x=x_loss,
|
||||
y=y_loss,
|
||||
mode="lines",
|
||||
name="亏损订单",
|
||||
line=dict(color="rgba(231, 76, 60, 0.7)", width=LINE_WIDTH),
|
||||
hoverinfo="text",
|
||||
text=[f"亏损: {abs(d[2]):.2f}" if d else None for d in customdata_loss],
|
||||
customdata=customdata_loss,
|
||||
hovertemplate=(
|
||||
"<b>%{text}</b><br>"
|
||||
"类型: %{customdata[3]}<br>"
|
||||
"开仓订单ID: %{customdata[0]}<极>"
|
||||
"平仓订单ID: %{customdata[1]}<extra></extra>"
|
||||
)
|
||||
))
|
||||
|
||||
|
||||
def generate_chart(kdf, odf, pnl_df, cum_realized, connections_df):
|
||||
"""生成K线图与交易标注"""
|
||||
fig = go.Figure()
|
||||
|
||||
# K线主图
|
||||
fig.add_trace(go.Candlestick(
|
||||
x=kdf["time"],
|
||||
open=kdf["open"],
|
||||
high=kdf["high"],
|
||||
low=kdf["low"],
|
||||
close=kdf["close"],
|
||||
name="K线",
|
||||
increasing_line_color="#2ecc71",
|
||||
decreasing_line_color="#e74c3c"
|
||||
))
|
||||
|
||||
# 添加交易点
|
||||
trade_types = [
|
||||
(odf[odf["side"] == "long_open"], "开多", "#2ecc71", "triangle-up"),
|
||||
(odf[odf["side"] == "long_close"], "平多", "#27ae60", "circle"),
|
||||
(odf[odf["side"] == "short_open"], "开空", "#e74c3c", "triangle-down"),
|
||||
(odf[odf["side"] == "short_close"], "平空", "#c0392b", "x")
|
||||
]
|
||||
|
||||
for data, name, color, symbol in trade_types:
|
||||
trace = create_trade_scatter(data, name, color, symbol)
|
||||
if trace:
|
||||
fig.add_trace(trace)
|
||||
|
||||
# 添加开平仓连接线
|
||||
add_trade_connections(fig, connections_df, odf)
|
||||
|
||||
# 计算时间范围,确保所有点都显示在图表中
|
||||
all_times = pd.concat([kdf["time"], odf["时间_x"]])
|
||||
min_time = all_times.min() - pd.Timedelta(minutes=10)
|
||||
max_time = all_times.max() + pd.Timedelta(minutes=10)
|
||||
|
||||
# 计算价格范围,确保所有点都显示在图表中
|
||||
min_price = min(kdf["low"].min(), odf["价格"].min()) * 0.99
|
||||
max_price = max(kdf["high"].max(), odf["价格"].max()) * 1.01
|
||||
|
||||
# 布局配置 - 更宽更扁的图表
|
||||
fig.update_layout(
|
||||
xaxis_title="时间",
|
||||
yaxis_title="价格 (USDT)",
|
||||
legend=dict(
|
||||
orientation="h",
|
||||
yanchor="bottom",
|
||||
y=1.02,
|
||||
xanchor="left",
|
||||
x=0,
|
||||
font=dict(size=FONT_SIZE, color=TEXT_COLOR) # 图例文字使用黑色
|
||||
),
|
||||
xaxis=dict(
|
||||
rangeslider=dict(visible=False),
|
||||
type="date",
|
||||
gridcolor="rgba(128, 128, 128, 0.2)",
|
||||
range=[min_time, max_time], # 设置时间范围
|
||||
title_font=dict(color=TEXT_COLOR), # 坐标轴标题使用黑色
|
||||
tickfont=dict(color=TEXT_COLOR) # 刻度标签使用黑色
|
||||
),
|
||||
yaxis=dict(
|
||||
gridcolor="rgba(128, 128, 128, 0.2)",
|
||||
range=[min_price, max_price], # 设置价格范围
|
||||
title_font=dict(color=TEXT_COLOR), # 坐标轴标题使用黑色
|
||||
tickfont=dict(color=TEXT_COLOR) # 刻度标签使用黑色
|
||||
),
|
||||
hovermode="x unified",
|
||||
hoverlabel=dict(
|
||||
namelength=-1,
|
||||
bgcolor="rgba(255, 255, 255, 0.9)",
|
||||
font_size=FONT_SIZE,
|
||||
font_color=TEXT_COLOR # 悬停标签文字使用黑色
|
||||
),
|
||||
margin=dict(l=50, r=50, t=80, b=50),
|
||||
plot_bgcolor="rgba(240, 240, 240, 1)",
|
||||
width=CHART_WIDTH, # 使用配置的宽度
|
||||
height=CHART_HEIGHT, # 使用配置的高度
|
||||
font=dict(size=FONT_SIZE, color=TEXT_COLOR), # 全局字体大小和颜色
|
||||
|
||||
# 增强交互性配置
|
||||
dragmode="pan", # 默认拖拽模式为平移
|
||||
clickmode="event+select", # 点击模式
|
||||
selectdirection="h", # 水平选择方向
|
||||
modebar=dict(
|
||||
orientation="h", # 水平方向工具栏
|
||||
bgcolor="rgba(255, 255, 255, 0.7)", # 半透明背景
|
||||
color="rgba(0, 0, 0, 0.7)", # 图标颜色
|
||||
activecolor="rgba(0, 0, 0, 0.9)" # 激活图标颜色
|
||||
)
|
||||
)
|
||||
|
||||
# 添加模式栏按钮
|
||||
fig.update_layout(
|
||||
modebar_add=[
|
||||
"zoom2d",
|
||||
"pan2d",
|
||||
"select2d",
|
||||
"lasso2d",
|
||||
"zoomIn2d",
|
||||
"zoomOut2d",
|
||||
"autoScale2d",
|
||||
"resetScale2d",
|
||||
"toImage"
|
||||
]
|
||||
)
|
||||
|
||||
# 配置缩放行为 - 确保滚轮缩放正常工作
|
||||
fig.update_xaxes(
|
||||
autorange=False,
|
||||
fixedrange=False, # 允许缩放
|
||||
constrain="domain", # 约束在域内
|
||||
rangeslider=dict(visible=False) # 禁用范围滑块
|
||||
)
|
||||
|
||||
fig.update_yaxes(
|
||||
autorange=False,
|
||||
fixedrange=False, # 允许缩放
|
||||
scaleanchor="x", # 保持纵横比
|
||||
scaleratio=1, # 缩放比例
|
||||
constrain="domain" # 约束在域内
|
||||
)
|
||||
|
||||
# 保存并打开结果 - 启用滚轮缩放
|
||||
fig.write_html(
|
||||
OUTPUT_HTML,
|
||||
include_plotlyjs="cdn",
|
||||
auto_open=True,
|
||||
config={
|
||||
'scrollZoom': True, # 启用滚轮缩放
|
||||
'displayModeBar': True, # 显示工具栏
|
||||
'displaylogo': False, # 隐藏Plotly标志
|
||||
'responsive': True # 响应式布局
|
||||
}
|
||||
)
|
||||
print(f"图表已生成: {OUTPUT_HTML}")
|
||||
|
||||
# 返回盈亏详情
|
||||
if not pnl_df.empty:
|
||||
pnl_df.to_csv("pnl_details.csv", index=False)
|
||||
print(f"盈亏详情已保存: pnl_details.csv")
|
||||
|
||||
if not connections_df.empty:
|
||||
connections_df.to_csv("trade_connections.csv", index=False)
|
||||
print(f"订单连接关系已保存: trade_connections.csv")
|
||||
|
||||
return fig
|
||||
|
||||
|
||||
# ========== 主执行流程 ==========
|
||||
def main():
|
||||
print("开始处理数据...")
|
||||
|
||||
# 加载数据
|
||||
kdf, median_step = load_kline_data()
|
||||
odf = load_order_data()
|
||||
|
||||
print(f"加载K线数据: {len(kdf)}条")
|
||||
print(f"加载订单数据: {len(odf)}条")
|
||||
|
||||
# 对齐交易时间
|
||||
odf = align_trades_to_candles(kdf, odf, median_step)
|
||||
|
||||
# 检查时间范围
|
||||
kline_min_time = kdf["time"].min()
|
||||
kline_max_time = kdf["time"].max()
|
||||
order_min_time = odf["时间"].min()
|
||||
order_max_time = odf["时间"].max()
|
||||
|
||||
print(f"K线时间范围: {kline_min_time} 至 {kline_max_time}")
|
||||
print(f"订单时间范围: {order_min_time} 至 {order_max_time}")
|
||||
|
||||
# 检查是否有订单在K线时间范围外
|
||||
outside_orders = odf[(odf["时间"] < kline_min_time) | (odf["时间"] > kline_max_time)]
|
||||
if not outside_orders.empty:
|
||||
print(f"警告: 有 {len(outside_orders)} 个订单在K线时间范围外")
|
||||
print(outside_orders[["时间", "方向", "价格"]])
|
||||
|
||||
# 计算盈亏和订单连接关系
|
||||
pnl_df, cum_realized, connections_df = calculate_pnl(odf)
|
||||
print(f"累计已实现盈亏: {cum_realized:.2f} USDT")
|
||||
print(f"订单连接关系: {len(connections_df)}条")
|
||||
|
||||
# 生成图表
|
||||
generate_chart(kdf, odf, pnl_df, cum_realized, connections_df)
|
||||
print("处理完成")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
main()
|
||||
713
价格展示/test3.py
713
价格展示/test3.py
@@ -1,713 +0,0 @@
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import plotly.graph_objects as go
|
||||
from datetime import datetime, timezone, timedelta
|
||||
import warnings
|
||||
import os
|
||||
import uuid
|
||||
|
||||
# ========== 配置 ==========
|
||||
KLINE_XLSX = "kline_data.xlsx" # K线数据文件名
|
||||
ORDERS_XLSX = "做市策略.xls" # 订单数据文件名
|
||||
OUTPUT_HTML = "kline_with_trades.html"
|
||||
SYMBOL = "ETH-USDT" # 交易对筛选
|
||||
|
||||
# 时间与对齐配置
|
||||
ORDERS_TIME_IS_LOCAL_ASIA_SH = True # 订单时间是否为东八区时间
|
||||
SNAP_TRADES_TO_NEAREST_CANDLE = True # 对齐交易点到最近的K线时间
|
||||
SNAP_TOLERANCE_MULTIPLIER = 1.5 # 对齐容忍度倍数
|
||||
|
||||
# 图表尺寸配置 - 更宽更扁
|
||||
CHART_WIDTH = 2200 # 更宽的图表
|
||||
CHART_HEIGHT = 600 # 更矮的图表
|
||||
FONT_SIZE = 12 # 字体大小
|
||||
ANNOTATION_FONT_SIZE = 10 # 标注字体大小
|
||||
MARKER_SIZE = 10 # 标记大小
|
||||
LINE_WIDTH = 1.5 # 连接线宽度
|
||||
|
||||
# 颜色配置 - 所有文本使用黑色
|
||||
TEXT_COLOR = "black" # 所有文本使用黑色
|
||||
TEXT_OFFSET = 10 # 文本偏移量(像素)
|
||||
|
||||
|
||||
# ========== 工具函数 ==========
|
||||
def parse_numeric(x):
|
||||
"""高效解析数值类型,支持多种格式"""
|
||||
if pd.isna(x):
|
||||
return np.nan
|
||||
try:
|
||||
# 尝试直接转换(大多数情况)
|
||||
return float(x)
|
||||
except:
|
||||
# 处理特殊格式
|
||||
s = str(x).replace(",", "").replace("USDT", "").replace("张", "").strip()
|
||||
if s.endswith("%"):
|
||||
s = s[:-1]
|
||||
return float(s) if s else np.nan
|
||||
|
||||
|
||||
def epoch_to_dt(x):
|
||||
"""将时间戳转换为上海时区时间"""
|
||||
try:
|
||||
return pd.to_datetime(int(x), unit="s", utc=True).tz_convert("Asia/Shanghai")
|
||||
except:
|
||||
return pd.NaT
|
||||
|
||||
|
||||
def zh_side(row):
|
||||
"""解析交易方向"""
|
||||
direction = str(row.get("方向", "")).strip()
|
||||
if "开多" in direction: return "long_open"
|
||||
if "平多" in direction: return "long_close"
|
||||
if "开空" in direction: return "short_open"
|
||||
if "平空" in direction: return "short_close"
|
||||
return "unknown"
|
||||
|
||||
|
||||
# ========== 数据加载与预处理 ==========
|
||||
def load_kline_data():
|
||||
"""加载并预处理K线数据"""
|
||||
if not os.path.exists(KLINE_XLSX):
|
||||
raise FileNotFoundError(f"K线数据文件不存在: {KLINE_XLSX}")
|
||||
|
||||
kdf = pd.read_excel(KLINE_XLSX, dtype=str)
|
||||
kdf.columns = [str(c).strip().lower() for c in kdf.columns]
|
||||
|
||||
# 验证必要列
|
||||
required_cols = {"id", "open", "close", "low", "high"}
|
||||
missing = required_cols - set(kdf.columns)
|
||||
if missing:
|
||||
raise ValueError(f"K线表缺少列: {missing}")
|
||||
|
||||
# 时间转换 - 确保id是秒级时间戳
|
||||
kdf["time"] = kdf["id"].apply(epoch_to_dt)
|
||||
|
||||
# 数值转换(向量化操作提升性能)
|
||||
for col in ["open", "close", "low", "high"]:
|
||||
kdf[col] = pd.to_numeric(kdf[col].apply(parse_numeric), errors="coerce")
|
||||
|
||||
# 清理无效数据
|
||||
kdf = kdf.dropna(subset=["time", "open", "close", "low", "high"])
|
||||
kdf = kdf.sort_values("time").reset_index(drop=True)
|
||||
|
||||
# 计算K线周期(用于交易点对齐)
|
||||
if len(kdf) >= 3:
|
||||
median_step = kdf["time"].diff().median()
|
||||
else:
|
||||
median_step = pd.Timedelta(minutes=1)
|
||||
|
||||
return kdf, median_step
|
||||
|
||||
|
||||
def load_order_data():
|
||||
"""加载并预处理订单数据"""
|
||||
if not os.path.exists(ORDERS_XLSX):
|
||||
raise FileNotFoundError(f"订单数据文件不存在: {ORDERS_XLSX}")
|
||||
|
||||
odf = pd.read_excel(ORDERS_XLSX, dtype=str)
|
||||
|
||||
# 验证必要列
|
||||
need_order_cols = ["时间", "交易对", "方向", "模式", "数量(张)", "成交价", "交易额", "消耗手续费", "用户盈亏"]
|
||||
missing = set(need_order_cols) - set(odf.columns)
|
||||
if missing:
|
||||
raise ValueError(f"订单表缺少列: {missing}")
|
||||
|
||||
# 筛选交易对
|
||||
if SYMBOL and "交易对" in odf.columns:
|
||||
odf = odf[odf["交易对"].astype(str).str.strip() == SYMBOL]
|
||||
|
||||
# 时间处理 - 确保时间格式正确
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
if ORDERS_TIME_IS_LOCAL_ASIA_SH:
|
||||
# 尝试多种格式解析时间
|
||||
odf["时间"] = pd.to_datetime(odf["时间"], errors="coerce", format="mixed")
|
||||
# 本地化为上海时区
|
||||
odf["时间"] = odf["时间"].dt.tz_localize("Asia/Shanghai", ambiguous="NaT", nonexistent="shift_forward")
|
||||
else:
|
||||
# 如果Excel时间已经是UTC
|
||||
odf["时间"] = pd.to_datetime(odf["时间"], utc=True, errors="coerce").dt.tz_convert("Asia/Shanghai")
|
||||
|
||||
# 数值转换
|
||||
numeric_cols = {
|
||||
"数量(张)": "数量",
|
||||
"成交价": "价格",
|
||||
"交易额": "交易额_num",
|
||||
"消耗手续费": "手续费",
|
||||
"用户盈亏": "盈亏"
|
||||
}
|
||||
|
||||
for src, dest in numeric_cols.items():
|
||||
odf[dest] = pd.to_numeric(odf[src].apply(parse_numeric), errors="coerce")
|
||||
|
||||
# 解析交易方向
|
||||
odf["side"] = odf.apply(zh_side, axis=1)
|
||||
|
||||
# 为每个订单生成唯一ID
|
||||
odf["order_id"] = [str(uuid.uuid4()) for _ in range(len(odf))]
|
||||
|
||||
# 计算本金(数量 * 价格)
|
||||
odf["本金"] = odf["数量"] * odf["价格"]
|
||||
|
||||
# 清理无效数据
|
||||
odf = odf.dropna(subset=["时间", "价格"])
|
||||
odf = odf.sort_values("时间").reset_index(drop=True)
|
||||
|
||||
return odf
|
||||
|
||||
|
||||
def align_trades_to_candles(kdf, odf, median_step):
|
||||
"""将交易点对齐到最近的K线时间"""
|
||||
if not SNAP_TRADES_TO_NEAREST_CANDLE or kdf.empty or odf.empty:
|
||||
return odf.assign(时间_x=odf["时间"])
|
||||
|
||||
snap_tolerance = pd.Timedelta(seconds=max(1, int(median_step.total_seconds() * SNAP_TOLERANCE_MULTIPLIER)))
|
||||
|
||||
# 使用merge_asof高效对齐 - 使用方向为'backward'确保交易点对齐到前一个K线
|
||||
anchor = kdf[["time"]].copy().rename(columns={"time": "k_time"})
|
||||
odf_sorted = odf.sort_values("时间")
|
||||
|
||||
# 关键优化:使用'backward'方向确保交易点对齐到前一个K线
|
||||
aligned = pd.merge_asof(
|
||||
odf_sorted,
|
||||
anchor,
|
||||
left_on="时间",
|
||||
right_on="k_time",
|
||||
direction="backward", # 使用'backward'确保交易点对齐到前一个K线
|
||||
tolerance=snap_tolerance
|
||||
)
|
||||
|
||||
# 保留原始时间作为参考
|
||||
aligned["原始时间"] = aligned["时间"]
|
||||
aligned["时间_x"] = aligned["k_time"].fillna(aligned["时间"])
|
||||
|
||||
return aligned
|
||||
|
||||
|
||||
# ========== 持仓跟踪与盈亏计算 ==========
|
||||
class PositionTracker:
|
||||
"""FIFO持仓跟踪器,支持订单走向可视化"""
|
||||
|
||||
def __init__(self):
|
||||
self.long_lots = [] # (数量, 价格, 时间, 手续费, 订单ID)
|
||||
self.short_lots = [] # (数量, 价格, 时间, 手续费, 订单ID)
|
||||
self.realized_pnl = 0.0
|
||||
self.history = [] # 记录所有交易历史
|
||||
self.trade_connections = [] # 记录开平仓连接关系
|
||||
|
||||
def open_long(self, qty, price, time, fee, order_id):
|
||||
"""开多仓"""
|
||||
if qty > 1e-9:
|
||||
self.long_lots.append((qty, price, time, fee, order_id))
|
||||
|
||||
def close_long(self, qty, price, time, fee, order_id):
|
||||
"""平多仓"""
|
||||
remaining = qty
|
||||
local_pnl = 0.0
|
||||
connections = [] # 本次平仓的连接关系
|
||||
|
||||
while remaining > 1e-9 and self.long_lots:
|
||||
lot_qty, lot_price, lot_time, lot_fee, open_order_id = self.long_lots[0]
|
||||
take = min(lot_qty, remaining)
|
||||
pnl = (price - lot_price) * take
|
||||
local_pnl += pnl
|
||||
lot_qty -= take
|
||||
remaining -= take
|
||||
|
||||
# 记录开平仓连接
|
||||
connection = {
|
||||
"open_time": lot_time,
|
||||
"close_time": time,
|
||||
"open_price": lot_price,
|
||||
"close_price": price,
|
||||
"qty": take,
|
||||
"pnl": pnl,
|
||||
"type": "long",
|
||||
"open_order_id": open_order_id,
|
||||
"close_order_id": order_id
|
||||
}
|
||||
self.trade_connections.append(connection)
|
||||
connections.append(connection)
|
||||
|
||||
# 记录平仓详情
|
||||
self.history.append({
|
||||
"开仓时间": lot_time,
|
||||
"平仓时间": time,
|
||||
"数量": take,
|
||||
"开仓价": lot_price,
|
||||
"平仓价": price,
|
||||
"盈亏": pnl,
|
||||
"类型": "平多",
|
||||
"开仓订单ID": open_order_id,
|
||||
"平仓订单ID": order_id
|
||||
})
|
||||
|
||||
if lot_qty <= 1e-9:
|
||||
self.long_lots.pop(0)
|
||||
else:
|
||||
self.long_lots[0] = (lot_qty, lot_price, lot_time, lot_fee, open_order_id)
|
||||
|
||||
local_pnl -= fee
|
||||
self.realized_pnl += local_pnl
|
||||
return local_pnl, connections
|
||||
|
||||
def open_short(self, qty, price, time, fee, order_id):
|
||||
"""开空仓"""
|
||||
if qty > 1e-9:
|
||||
self.short_lots.append((qty, price, time, fee, order_id))
|
||||
|
||||
def close_short(self, qty, price, time, fee, order_id):
|
||||
"""平空仓"""
|
||||
remaining = qty
|
||||
local_pnl = 0.0
|
||||
connections = [] # 本次平仓的连接关系
|
||||
|
||||
while remaining > 1e-9 and self.short_lots:
|
||||
lot_qty, lot_price, lot_time, lot_fee, open_order_id = self.short_lots[0]
|
||||
take = min(lot_qty, remaining)
|
||||
pnl = (lot_price - price) * take
|
||||
local_pnl += pnl
|
||||
lot_qty -= take
|
||||
remaining -= take
|
||||
|
||||
# 记录开平仓连接
|
||||
connection = {
|
||||
"open_time": lot_time,
|
||||
"close_time": time,
|
||||
"open_price": lot_price,
|
||||
"close_price": price,
|
||||
"q极": take,
|
||||
"pnl": pnl,
|
||||
"type": "short",
|
||||
"open_order_id": open_order_id,
|
||||
"close_order_id": order_id
|
||||
}
|
||||
self.trade_connections.append(connection)
|
||||
connections.append(connection)
|
||||
|
||||
# 记录平仓详情
|
||||
self.history.append({
|
||||
"开仓时间": lot_time,
|
||||
"平仓时间": time,
|
||||
"数量": take,
|
||||
"开仓价": lot_price,
|
||||
"平仓价": price,
|
||||
"盈亏": pnl,
|
||||
"类型": "平空",
|
||||
"开仓订单ID": open_order_id,
|
||||
"平仓订单ID": order_id
|
||||
})
|
||||
|
||||
if lot_qty <= 1e-9:
|
||||
self.short_lots.pop(0)
|
||||
else:
|
||||
self.short_lots[0] = (lot_qty, lot_price, lot_time, lot_fee, open_order_id)
|
||||
|
||||
local_pnl -= fee
|
||||
self.realized_pnl += local_pnl
|
||||
return local_pnl, connections
|
||||
|
||||
|
||||
def calculate_pnl(odf):
|
||||
"""计算持仓盈亏和订单连接关系"""
|
||||
tracker = PositionTracker()
|
||||
all_connections = []
|
||||
|
||||
for idx, r in odf.iterrows():
|
||||
qty = r["数量"]
|
||||
price = r["价格"]
|
||||
ts = r["时间"]
|
||||
fee = r["手续费"]
|
||||
side = r["side"]
|
||||
order_id = r["order_id"]
|
||||
|
||||
if side == "long_open":
|
||||
tracker.open_long(qty, price, ts, fee, order_id)
|
||||
elif side == "long_close":
|
||||
_, connections = tracker.close_long(qty, price, ts, fee, order_id)
|
||||
all_connections.extend(connections)
|
||||
elif side == "short_open":
|
||||
tracker.open_short(qty, price, ts, fee, order_id)
|
||||
elif side == "short_close":
|
||||
_, connections = tracker.close_short(qty, price, ts, fee, order_id)
|
||||
all_connections.extend(connections)
|
||||
|
||||
# 创建盈亏DataFrame
|
||||
if tracker.history:
|
||||
pnl_df = pd.DataFrame(tracker.history)
|
||||
# 添加对齐后的时间
|
||||
pnl_df["时间_x"] = pnl_df["平仓时间"].apply(
|
||||
lambda x: odf.loc[odf["时间"] == x, "时间_x"].values[0] if not odf.empty else x
|
||||
)
|
||||
else:
|
||||
pnl_df = pd.DataFrame()
|
||||
|
||||
# 创建连接关系DataFrame
|
||||
connections_df = pd.DataFrame(all_connections) if all_connections else pd.DataFrame()
|
||||
|
||||
return pnl_df, tracker.realized_pnl, connections_df
|
||||
|
||||
|
||||
# ========== 可视化 ==========
|
||||
def create_trade_scatter(df, name, color, symbol):
|
||||
"""创建交易点散点图"""
|
||||
if df.empty:
|
||||
return None
|
||||
|
||||
# 为不同类型的交易点创建不同的文本标签
|
||||
if name == "开多":
|
||||
text = "开多\n" + df["价格"].apply(lambda x: f"{x:.2f}") + "\n" + df["本金"].apply(lambda x: f"{x:.0f}")
|
||||
elif name == "平多":
|
||||
text = "平多\n" + df["价格"].apply(lambda x: f"{x:.2f}") + "\n" + df["盈亏"].apply(lambda x: f"{x:.0f}")
|
||||
elif name == "开空":
|
||||
text = "开空\n" + df["价格"].apply(lambda x: f"{x:.2f}") + "\n" + df["本金"].apply(lambda x: f"{x:.0f}")
|
||||
elif name == "平空":
|
||||
text = "平空\n" + df["价格"].apply(lambda x: f"{x:.2f}") + "\n" + df["盈亏"].apply(lambda x: f"{x:.0f}")
|
||||
else:
|
||||
text = name
|
||||
|
||||
return go.Scatter(
|
||||
x=df["时间_x"],
|
||||
y=df["价格"],
|
||||
mode="markers+text",
|
||||
name=name,
|
||||
text=text,
|
||||
textposition="middle right", # 文本放在右侧中间位置
|
||||
textfont=dict(size=ANNOTATION_FONT_SIZE, color=TEXT_COLOR), # 使用黑色文本
|
||||
marker=dict(
|
||||
size=MARKER_SIZE,
|
||||
color=color,
|
||||
symbol=symbol,
|
||||
line=dict(width=1.5, color="black")
|
||||
),
|
||||
customdata=np.stack([
|
||||
df["数量"].to_numpy(),
|
||||
df["价格"].to_numpy(),
|
||||
df["手续费"].to_numpy(),
|
||||
df.get("盈亏", np.nan).to_numpy(),
|
||||
df.get("原始时间", df["时间"]).dt.strftime("%Y-%m-%d %H:%M:%S").to_numpy(),
|
||||
df["order_id"].to_numpy(),
|
||||
df["本金"].to_numpy()
|
||||
], axis=-1),
|
||||
hovertemplate=(
|
||||
f"<b>{name}</b><br>"
|
||||
"数量: %{customdata[0]:.0f}张<br>"
|
||||
"价格: %{customdata[1]:.2f}<br>"
|
||||
"手续费: %{customdata[2]:.6f}<br>"
|
||||
"盈亏: %{customdata[3]:.4f}<br>"
|
||||
"本金: %{customdata[6]:.0f}<br>"
|
||||
"时间: %{customdata[4]}<br>"
|
||||
"订单ID: %{customdata[5]}<extra></extra>"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def add_trade_connections(fig, connections_df, odf):
|
||||
"""添加开平仓连接线"""
|
||||
if connections_df.empty:
|
||||
return
|
||||
|
||||
# 为盈利和亏损的连接线分别创建轨迹
|
||||
profit_lines = []
|
||||
loss_lines = []
|
||||
|
||||
for _, conn in connections_df.iterrows():
|
||||
# 获取开仓点和平仓点的坐标
|
||||
open_point = odf[odf["order_id"] == conn["open_order_id"]].iloc[0]
|
||||
close_point = odf[odf["order_id"] == conn["close_order_id"]].iloc[0]
|
||||
|
||||
line_data = {
|
||||
"x": [open_point["时间_x"], close_point["时间_x"]],
|
||||
"y": [open_point["价格"], close_point["价格"]],
|
||||
"pnl": conn["pnl"],
|
||||
"type": conn["type"],
|
||||
"open_order_id": conn["open_order_id"],
|
||||
"close_order_id": conn["close_order_id"]
|
||||
}
|
||||
|
||||
if conn["pnl"] >= 0:
|
||||
profit_lines.append(line_data)
|
||||
else:
|
||||
loss_lines.append(line_data)
|
||||
|
||||
# 添加盈利连接线(绿色)
|
||||
if profit_lines:
|
||||
x_profit = []
|
||||
y_profit = []
|
||||
customdata_profit = []
|
||||
|
||||
for line in profit_lines:
|
||||
x_profit.extend(line["x"])
|
||||
y_profit.extend(line["y"])
|
||||
x_profit.append(None)
|
||||
y_profit.append(None)
|
||||
|
||||
# 为每个点添加自定义数据
|
||||
customdata_profit.append([
|
||||
line["open_order_id"],
|
||||
line["close_order_id"],
|
||||
line["pnl"],
|
||||
line["type"]
|
||||
])
|
||||
customdata_profit.append([
|
||||
line["open_order_id"],
|
||||
line["close_order_id"],
|
||||
line["pnl"],
|
||||
line["type"]
|
||||
])
|
||||
customdata_profit.append(None)
|
||||
|
||||
fig.add_trace(go.Scatter(
|
||||
x=x_profit,
|
||||
y=y_profit,
|
||||
mode="lines",
|
||||
name="盈利订单",
|
||||
line=dict(color="rgba(46, 204, 113, 0.7)", width=LINE_WIDTH),
|
||||
hoverinfo="text",
|
||||
text=[f"盈利: {d[2]:.2f}" if d else None for d in customdata_profit],
|
||||
customdata=customdata_profit,
|
||||
hovertemplate=(
|
||||
"<b>%{text}</b><br>"
|
||||
"类型: %{customdata[3]}<br>"
|
||||
"开仓订单ID: %{customdata[0]}<br>"
|
||||
"平仓订单ID: %{customdata[1]}<extra></extra>"
|
||||
)
|
||||
))
|
||||
|
||||
# 添加亏损连接线(红色)
|
||||
if loss_lines:
|
||||
x_loss = []
|
||||
y_loss = []
|
||||
customdata_loss = []
|
||||
|
||||
for line in loss_lines:
|
||||
x_loss.extend(line["x"])
|
||||
y_loss.extend(line["y"])
|
||||
x_loss.append(None)
|
||||
y_loss.append(None)
|
||||
|
||||
# 为每个点添加自定义数据
|
||||
customdata_loss.append([
|
||||
line["open_order_id"],
|
||||
line["close_order_id"],
|
||||
line["pnl"],
|
||||
line["type"]
|
||||
])
|
||||
customdata_loss.append([
|
||||
line["open_order_id"],
|
||||
line["close_order_id"],
|
||||
line["pnl"],
|
||||
line["type"]
|
||||
])
|
||||
customdata_loss.append(None)
|
||||
|
||||
fig.add_trace(go.Scatter(
|
||||
x=x_loss,
|
||||
y=y_loss,
|
||||
mode="lines",
|
||||
name="亏损订单",
|
||||
line=dict(color="rgba(231, 76, 60, 0.7)", width=LINE_WIDTH),
|
||||
hoverinfo="text",
|
||||
text=[f"亏损: {abs(d[2]):.2f}" if d else None for d in customdata_loss],
|
||||
customdata=customdata_loss,
|
||||
hovertemplate=(
|
||||
"<b>%{text}</b><br>"
|
||||
"类型: %{customdata[3]}<br>"
|
||||
"开仓订单ID: %{customdata[0]}<极>"
|
||||
"平仓订单ID: %{customdata[1]}<extra></extra>"
|
||||
)
|
||||
))
|
||||
|
||||
|
||||
def generate_chart(kdf, odf, pnl_df, cum_realized, connections_df):
|
||||
"""生成K线图与交易标注"""
|
||||
fig = go.Figure()
|
||||
|
||||
# K线主图
|
||||
fig.add_trace(go.Candlestick(
|
||||
x=kdf["time"],
|
||||
open=kdf["open"],
|
||||
high=kdf["high"],
|
||||
low=kdf["low"],
|
||||
close=kdf["close"],
|
||||
name="K线",
|
||||
increasing_line_color="#2ecc71",
|
||||
decreasing_line_color="#e74c3c"
|
||||
))
|
||||
|
||||
# 添加交易点
|
||||
trade_types = [
|
||||
(odf[odf["side"] == "long_open"], "开多", "#2ecc71", "triangle-up"),
|
||||
(odf[odf["side"] == "long_close"], "平多", "#27ae60", "circle"),
|
||||
(odf[odf["side"] == "short_open"], "开空", "#e74c3c", "triangle-down"),
|
||||
(odf[odf["side"] == "short_close"], "平空", "#c0392b", "x")
|
||||
]
|
||||
|
||||
for data, name, color, symbol in trade_types:
|
||||
trace = create_trade_scatter(data, name, color, symbol)
|
||||
if trace:
|
||||
fig.add_trace(trace)
|
||||
|
||||
# 添加开平仓连接线
|
||||
add_trade_connections(fig, connections_df, odf)
|
||||
|
||||
# 计算时间范围,确保所有点都显示在图表中
|
||||
all_times = pd.concat([kdf["time"], odf["时间_x"]])
|
||||
min_time = all_times.min() - pd.Timedelta(minutes=10)
|
||||
max_time = all_times.max() + pd.Timedelta(minutes=10)
|
||||
|
||||
# 计算价格范围,确保所有点都显示在图表中
|
||||
min_price = min(kdf["low"].min(), odf["价格"].min()) * 0.999
|
||||
max_price = max(kdf["high"].max(), odf["价格"].max()) * 1.001
|
||||
|
||||
# 布局配置 - 更宽更扁的图表
|
||||
fig.update_layout(
|
||||
xaxis_title="时间",
|
||||
yaxis_title="价格 (USDT)",
|
||||
legend=dict(
|
||||
orientation="h",
|
||||
yanchor="bottom",
|
||||
y=1.02,
|
||||
xanchor="left",
|
||||
x=0,
|
||||
font=dict(size=FONT_SIZE, color=TEXT_COLOR) # 图例文字使用黑色
|
||||
),
|
||||
xaxis=dict(
|
||||
rangeslider=dict(visible=False),
|
||||
type="date",
|
||||
gridcolor="rgba(128, 128, 128, 0.2)",
|
||||
range=[min_time, max_time], # 设置时间范围
|
||||
title_font=dict(color=TEXT_COLOR), # 坐标轴标题使用黑色
|
||||
tickfont=dict(color=TEXT_COLOR) # 刻度标签使用黑色
|
||||
),
|
||||
yaxis=dict(
|
||||
gridcolor="rgba(128, 128, 128, 0.2)",
|
||||
range=[min_price, max_price], # 设置价格范围
|
||||
title_font=dict(color=TEXT_COLOR), # 坐标轴标题使用黑色
|
||||
tickfont=dict(color=TEXT_COLOR) # 刻度标签使用黑色
|
||||
),
|
||||
hovermode="x unified",
|
||||
hoverlabel=dict(
|
||||
namelength=-1,
|
||||
bgcolor="rgba(255, 255, 255, 0.9)",
|
||||
font_size=FONT_SIZE,
|
||||
font_color=TEXT_COLOR # 悬停标签文字使用黑色
|
||||
),
|
||||
margin=dict(l=50, r=50, t=80, b=50),
|
||||
plot_bgcolor="rgba(240, 240, 240, 1)",
|
||||
width=CHART_WIDTH, # 使用配置的宽度
|
||||
height=CHART_HEIGHT, # 使用配置的高度
|
||||
font=dict(size=FONT_SIZE, color=TEXT_COLOR), # 全局字体大小和颜色
|
||||
|
||||
# 增强交互性配置
|
||||
dragmode="pan", # 默认拖拽模式为平移
|
||||
clickmode="event+select", # 点击模式
|
||||
selectdirection="h", # 水平选择方向
|
||||
modebar=dict(
|
||||
orientation="h", # 水平方向工具栏
|
||||
bgcolor="rgba(255, 255, 255, 0.7)", # 半透明背景
|
||||
color="rgba(0, 0, 0, 0.7)", # 图标颜色
|
||||
activecolor="rgba(0, 0, 0, 0.9)" # 激活图标颜色
|
||||
)
|
||||
)
|
||||
|
||||
# 添加模式栏按钮
|
||||
fig.update_layout(
|
||||
modebar_add=[
|
||||
"zoom2d",
|
||||
"pan2d",
|
||||
"select2d",
|
||||
"lasso2d",
|
||||
"zoomIn2d",
|
||||
"zoomOut2d",
|
||||
"autoScale2d",
|
||||
"resetScale2d",
|
||||
"toImage"
|
||||
]
|
||||
)
|
||||
|
||||
# 配置缩放行为 - 确保滚轮缩放正常工作
|
||||
fig.update_xaxes(
|
||||
autorange=False,
|
||||
fixedrange=False, # 允许缩放
|
||||
constrain="domain", # 约束在域内
|
||||
rangeslider=dict(visible=False) # 禁用范围滑块
|
||||
)
|
||||
|
||||
fig.update_yaxes(
|
||||
autorange=False,
|
||||
fixedrange=False, # 允许缩放
|
||||
scaleanchor="x", # 保持纵横比
|
||||
scaleratio=1, # 缩放比例
|
||||
constrain="domain" # 约束在域内
|
||||
)
|
||||
|
||||
# 保存并打开结果 - 启用滚轮缩放
|
||||
fig.write_html(
|
||||
OUTPUT_HTML,
|
||||
include_plotlyjs="cdn",
|
||||
auto_open=True,
|
||||
config={
|
||||
'scrollZoom': True, # 启用滚轮缩放
|
||||
'displayModeBar': True, # 显示工具栏
|
||||
'displaylogo': False, # 隐藏Plotly标志
|
||||
'responsive': True # 响应式布局
|
||||
}
|
||||
)
|
||||
print(f"图表已生成: {OUTPUT_HTML}")
|
||||
|
||||
# 返回盈亏详情
|
||||
if not pnl_df.empty:
|
||||
pnl_df.to_csv("pnl_details.csv", index=False)
|
||||
print(f"盈亏详情已保存: pnl_details.csv")
|
||||
|
||||
if not connections_df.empty:
|
||||
connections_df.to_csv("trade_connections.csv", index=False)
|
||||
print(f"订单连接关系已保存: trade_connections.csv")
|
||||
|
||||
return fig
|
||||
|
||||
|
||||
# ========== 主执行流程 ==========
|
||||
def main():
|
||||
print("开始处理数据...")
|
||||
|
||||
# 加载数据
|
||||
kdf, median_step = load_kline_data()
|
||||
odf = load_order_data()
|
||||
|
||||
print(f"加载K线数据: {len(kdf)}条")
|
||||
print(f"加载订单数据: {len(odf)}条")
|
||||
|
||||
# 对齐交易时间
|
||||
odf = align_trades_to_candles(kdf, odf, median_step)
|
||||
|
||||
# 检查时间范围
|
||||
kline_min_time = kdf["time"].min()
|
||||
kline_max_time = kdf["time"].max()
|
||||
order_min_time = odf["时间"].min()
|
||||
order_max_time = odf["时间"].max()
|
||||
|
||||
print(f"K线时间范围: {kline_min_time} 至 {kline_max_time}")
|
||||
print(f"订单时间范围: {order_min_time} 至 {order_max_time}")
|
||||
|
||||
# 检查是否有订单在K线时间范围外
|
||||
outside_orders = odf[(odf["时间"] < kline_min_time) | (odf["时间"] > kline_max_time)]
|
||||
if not outside_orders.empty:
|
||||
print(f"警告: 有 {len(outside_orders)} 个订单在K线时间范围外")
|
||||
print(outside_orders[["时间", "方向", "价格"]])
|
||||
|
||||
# 计算盈亏和订单连接关系
|
||||
pnl_df, cum_realized, connections_df = calculate_pnl(odf)
|
||||
print(f"累计已实现盈亏: {cum_realized:.2f} USDT")
|
||||
print(f"订单连接关系: {len(connections_df)}条")
|
||||
|
||||
# 生成图表
|
||||
generate_chart(kdf, odf, pnl_df, cum_realized, connections_df)
|
||||
print("处理完成")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore")
|
||||
main()
|
||||
@@ -1,277 +0,0 @@
|
||||
open_time,close_time,open_price,close_price,q极,pnl,type,open_order_id,close_order_id,qty
|
||||
2025-08-28 09:35:18+08:00,2025-08-28 09:44:18+08:00,4514.69,4507.43,664.0,4820.639999999541,short,e1d3a2da-219e-436f-ac9e-a7ea93de15bd,abfa0508-9399-4f1b-90c7-7716c3e7f511,
|
||||
2025-08-28 10:06:42+08:00,2025-08-28 10:10:07+08:00,4519.88,4515.64,,-4689.439999999759,long,594bd10c-2120-4692-a8eb-9f5325e26ef0,bd643cab-14d4-414d-912e-e9257ddb96de,1106.0
|
||||
2025-08-28 10:45:07+08:00,2025-08-28 10:51:41+08:00,4512.13,4516.84,1108.0,-5218.68000000004,short,fc0c73ab-9fff-41b5-98c1-ef6f7d300256,206688da-848a-4822-9571-c05302ab7a29,
|
||||
2025-08-28 11:07:30+08:00,2025-08-28 11:13:19+08:00,4525.0,4529.25,,850.0,long,6197fb5c-6202-422f-9012-5395c5393fb3,59d8859f-2b64-4e8b-aefe-57998b198841,200.0
|
||||
2025-08-28 11:07:30+08:00,2025-08-28 11:13:19+08:00,4525.0,4529.25,,425.0,long,794c0925-6e48-4b9a-a162-d31d7a3c42cc,59d8859f-2b64-4e8b-aefe-57998b198841,100.0
|
||||
2025-08-28 11:07:31+08:00,2025-08-28 11:13:19+08:00,4525.0,4529.25,,2052.75,long,65f7d764-ad05-40c7-bce1-6b227ad5f5c0,59d8859f-2b64-4e8b-aefe-57998b198841,483.0
|
||||
2025-08-28 11:07:31+08:00,2025-08-28 11:13:19+08:00,4525.0,4529.25,,425.0,long,35e1c160-4129-425a-ae46-b8fb45d53ec8,59d8859f-2b64-4e8b-aefe-57998b198841,100.0
|
||||
2025-08-28 11:28:58+08:00,2025-08-28 11:34:21+08:00,4533.74,4537.34,,1440.0000000001455,long,a983b8f5-70d1-441b-87df-397c6c1ce843,3e161e4e-f367-421a-bc0d-8095f7b79675,400.0
|
||||
2025-08-28 11:28:58+08:00,2025-08-28 11:34:21+08:00,4533.74,4537.34,,1735.2000000001754,long,a983b8f5-70d1-441b-87df-397c6c1ce843,45ddb145-d637-4985-9c4b-3dee2fd189b5,482.0
|
||||
2025-08-28 12:22:53+08:00,2025-08-28 12:23:33+08:00,4550.8,4545.04,,-2753.2800000001043,long,37d97b24-c1fd-4a2d-ba0e-75b818062686,fcfe39c7-0bcf-47f6-9b48-a1dd70bd5444,478.0
|
||||
2025-08-28 12:22:53+08:00,2025-08-28 12:23:33+08:00,4550.8,4545.04,,-2304.0000000000873,long,37d97b24-c1fd-4a2d-ba0e-75b818062686,867b1b2c-0fe5-466b-a89f-ec8dce25bcda,400.0
|
||||
2025-08-28 12:26:39+08:00,2025-08-28 12:34:19+08:00,4549.6,4555.44,,5133.3599999993285,long,0a3a08a1-61c7-4997-b13c-4da05b2c5dbc,ad149e7f-7088-4dd9-9698-9b7bd615478e,879.0
|
||||
2025-08-28 12:46:19+08:00,2025-08-28 12:48:10+08:00,4561.8,4567.53,600.0,-3437.999999999738,short,18929fa1-2cc7-4e7f-8e76-c3937f16f877,8283a8b1-17f1-4818-843d-33877996af35,
|
||||
2025-08-28 12:46:19+08:00,2025-08-28 12:48:10+08:00,4561.8,4567.53,276.0,-1581.4799999998795,short,18929fa1-2cc7-4e7f-8e76-c3937f16f877,c0a1d021-0708-476e-a766-722bc73ab740,
|
||||
2025-08-28 12:53:40+08:00,2025-08-28 12:57:03+08:00,4577.05,4569.51,873.0,6582.419999999968,short,16f8970d-ef44-4026-ba19-9c9ff6dc70e1,e1f9de7d-115e-4289-8acd-bbba16e04ff0,
|
||||
2025-08-28 12:58:03+08:00,2025-08-28 13:01:30+08:00,4567.45,4572.58,475.0,-2436.750000000052,short,8dbfcfc8-466c-4111-acdf-316a2a8c7428,3e0a22b4-7707-4332-b80d-87d3b07cf3cc,
|
||||
2025-08-28 12:58:03+08:00,2025-08-28 13:01:30+08:00,4567.45,4572.58,400.0,-2052.0000000000437,short,8dbfcfc8-466c-4111-acdf-316a2a8c7428,29775823-c3fc-4dbc-8afa-f9587f803f2a,
|
||||
2025-08-28 13:32:49+08:00,2025-08-28 13:41:18+08:00,4564.18,4565.88,,1225.6999999998689,long,6c0a64d8-b890-42bc-ae09-152919931d57,5bba77bd-b49f-4c6d-9a12-fe062c687cae,721.0
|
||||
2025-08-28 13:32:49+08:00,2025-08-28 13:41:18+08:00,4564.18,4565.88,,263.4999999999718,long,c0e17c5e-6a39-465c-a3b3-347f1af3d772,5bba77bd-b49f-4c6d-9a12-fe062c687cae,155.0
|
||||
2025-08-28 13:52:44+08:00,2025-08-28 13:57:25+08:00,4555.2,4559.95,400.0,-1900.0,short,8f641745-890a-4b9c-8d83-10163234429d,dd5f8c6a-74da-4984-aa24-eebfbceb6d22,
|
||||
2025-08-28 13:52:44+08:00,2025-08-28 13:57:25+08:00,4555.2,4559.95,478.0,-2270.5,short,66c5bcfb-02ef-4a8c-a314-bd9d672decb5,dd5f8c6a-74da-4984-aa24-eebfbceb6d22,
|
||||
2025-08-28 14:40:01+08:00,2025-08-28 14:41:47+08:00,4572.59,4578.98,874.0,-5584.859999999491,short,1b1efa90-7dd5-4b5f-a170-eb87df7fb7db,26b0c5e5-f4a4-4f47-9fdc-12c6ecd05704,
|
||||
2025-08-28 15:05:18+08:00,2025-08-28 15:07:11+08:00,4582.63,4577.98,,-279.00000000003274,long,61f7b24d-18b1-44ee-bc9e-60666e4b2346,e53afe94-f3fa-42c1-9e49-619cd77261ab,60.0
|
||||
2025-08-28 15:05:18+08:00,2025-08-28 15:07:11+08:00,4582.63,4578.03,,-2760.0000000002183,long,61f7b24d-18b1-44ee-bc9e-60666e4b2346,b9cf7c9c-d909-4dad-ae66-64aa0706566f,600.0
|
||||
2025-08-28 15:05:18+08:00,2025-08-28 15:07:11+08:00,4582.63,4577.52,,-1083.3199999999306,long,61f7b24d-18b1-44ee-bc9e-60666e4b2346,05d46d57-d923-4d18-8c18-5a49927ac910,212.0
|
||||
2025-08-28 15:22:41+08:00,2025-08-28 15:23:11+08:00,4572.88,4567.91,,-2982.000000000153,long,184c5b21-ed98-43c2-8003-8cc5f8c1bb0a,10d151ec-2891-44fe-8527-89643b3f3b46,600.0
|
||||
2025-08-28 15:22:41+08:00,2025-08-28 15:23:11+08:00,4572.88,4567.53,,-1465.9000000000997,long,184c5b21-ed98-43c2-8003-8cc5f8c1bb0a,2a5ef255-f800-4e98-a10c-ca3ed33bb372,274.0
|
||||
2025-08-28 15:57:55+08:00,2025-08-28 16:02:09+08:00,4589.48,4607.47,,9840.530000000377,long,6fdd3eb9-13a9-4e2a-90b6-78e7d71f3fbc,81bc2d5e-8099-4d48-b968-d695a7f1fee4,547.0
|
||||
2025-08-28 15:57:55+08:00,2025-08-28 16:02:09+08:00,4589.48,4607.47,,5828.760000000224,long,aec18848-d3b5-4786-8c51-a4fdfc454ee2,81bc2d5e-8099-4d48-b968-d695a7f1fee4,324.0
|
||||
2025-08-28 16:05:54+08:00,2025-08-28 16:09:19+08:00,4614.86,4624.89,,8685.980000000567,long,8e6d3e81-c813-4759-b608-9ed4e547b24d,25509f73-848d-4620-b662-f7f93d76becc,866.0
|
||||
2025-08-28 16:09:50+08:00,2025-08-28 16:15:00+08:00,4621.56,4617.66,,-3373.500000000472,long,29739f4f-b289-4a1e-a5e7-f6925dd72b4c,7e4c74ac-902f-47c5-9e0a-b2ef07f31e8e,865.0
|
||||
2025-08-28 16:23:59+08:00,2025-08-28 16:28:31+08:00,4609.17,4602.01,867.0,6207.719999999874,short,6e8dc270-512a-4826-84c9-cfdf82c6cd59,0df8cd52-9db6-4532-99e1-923fcd9c134d,
|
||||
2025-08-28 16:54:47+08:00,2025-08-28 16:58:41+08:00,4595.28,4599.69,870.0,-3836.6999999998734,short,35598fc3-567f-4ee2-a9fe-db14aab8228b,9777e233-baa8-4238-8f48-ef3e303ff883,
|
||||
2025-08-28 17:06:53+08:00,2025-08-28 17:15:11+08:00,4595.19,4592.66,870.0,2201.0999999997784,short,3432672f-ced3-401f-981d-7686ddd396e6,bf9ffdef-56bd-4d35-9eef-f92a668d5829,
|
||||
2025-08-28 17:43:59+08:00,2025-08-28 17:49:12+08:00,4584.79,4581.95,258.0,732.7200000000375,short,c0b26494-b8c4-434e-bc49-e3b317c1fa88,ffbc0ba2-aa7f-4377-bade-cf31e3697b19,
|
||||
2025-08-28 17:43:59+08:00,2025-08-28 17:49:12+08:00,4584.79,4581.95,114.0,323.7600000000166,short,6fdb9fc7-e0f4-4033-896b-a13160b4fd18,ffbc0ba2-aa7f-4377-bade-cf31e3697b19,
|
||||
2025-08-28 17:43:59+08:00,2025-08-28 17:49:12+08:00,4584.79,4581.95,500.0,1420.0000000000728,short,b064c485-653b-4f88-a0d1-86961284dc70,ffbc0ba2-aa7f-4377-bade-cf31e3697b19,
|
||||
2025-08-28 17:49:44+08:00,2025-08-28 17:50:54+08:00,4579.81,4584.85,500.0,-2519.999999999982,short,1f97429c-1de1-467a-8985-8f9bb17000b5,cf389028-3cfb-4521-bf8c-59c3d9782e85,
|
||||
2025-08-28 17:49:44+08:00,2025-08-28 17:50:54+08:00,4579.81,4584.85,139.0,-700.559999999995,short,989c44a3-a81f-4939-ab13-d9884d845a65,cf389028-3cfb-4521-bf8c-59c3d9782e85,
|
||||
2025-08-28 17:49:44+08:00,2025-08-28 17:50:54+08:00,4579.58,4584.85,234.0,-1233.1800000001022,short,0b83f7ef-0985-4168-a211-d009f6c6ff1a,cf389028-3cfb-4521-bf8c-59c3d9782e85,
|
||||
2025-08-28 18:36:51+08:00,2025-08-28 18:43:09+08:00,4609.97,4604.0,,-3182.0100000001357,long,b8391ca4-74b4-41bc-85cd-b4c82cf6e8f3,2926d3da-8a19-4fae-938f-e2556e994376,533.0
|
||||
2025-08-28 18:36:51+08:00,2025-08-28 18:43:09+08:00,4610.0,4604.0,,-504.0,long,e441f55d-693a-4301-b6c9-10d54ab10357,2926d3da-8a19-4fae-938f-e2556e994376,84.0
|
||||
2025-08-28 18:36:51+08:00,2025-08-28 18:43:09+08:00,4609.97,4604.0,,-197.0100000000084,long,9dbce008-1d12-422c-80f8-a2c928610074,2926d3da-8a19-4fae-938f-e2556e994376,33.0
|
||||
2025-08-28 19:46:14+08:00,2025-08-28 19:54:40+08:00,4586.91,4591.36,654.0,-2910.299999999881,short,005b10f4-329d-4d34-8263-b8f63f526ac8,ea07e34c-cd41-4c80-b13f-6b8308257b92,
|
||||
2025-08-28 20:30:04+08:00,2025-08-28 20:30:34+08:00,4587.25,4588.6,,697.9500000001881,long,26fe1b1b-f4f0-4c49-b6d0-f8445d4e996e,9011ad7e-ef83-4c62-ac90-3e538965ad3f,517.0
|
||||
2025-08-28 20:30:05+08:00,2025-08-28 20:30:34+08:00,4587.44,4588.6,,410.64000000027045,long,32f1db9d-6a9b-413f-abba-3f698a83ec65,9011ad7e-ef83-4c62-ac90-3e538965ad3f,354.0
|
||||
2025-08-28 20:31:22+08:00,2025-08-28 20:31:28+08:00,4583.96,4585.9,400.0,-775.9999999998399,short,dd7d8a55-c4bf-4ff8-82b9-ec8591888b0d,ca1e611c-1ba6-4418-9f86-8e492a7726c5,
|
||||
2025-08-28 20:31:22+08:00,2025-08-28 20:31:29+08:00,4583.96,4585.9,472.0,-915.6799999998111,short,dd7d8a55-c4bf-4ff8-82b9-ec8591888b0d,04792496-7da0-4a9b-95cf-293b41d8292a,
|
||||
2025-08-28 20:43:41+08:00,2025-08-28 20:46:26+08:00,4609.77,4603.6,,-567.6400000000067,long,da034c24-f333-4197-ae9d-7dfa8639be45,9d59670d-a478-4432-8ec0-e3a8c4ec4d8a,92.0
|
||||
2025-08-28 20:43:41+08:00,2025-08-28 20:46:26+08:00,4609.77,4603.6,,-3702.0000000000437,long,da034c24-f333-4197-ae9d-7dfa8639be45,e83ff9d3-3078-4f5c-8e6a-fa5750c97b55,600.0
|
||||
2025-08-28 20:43:41+08:00,2025-08-28 20:46:27+08:00,4609.77,4604.08,,-995.7500000000891,long,da034c24-f333-4197-ae9d-7dfa8639be45,433dc03b-8b85-4951-9f21-dfaa5d6a0de5,175.0
|
||||
2025-08-29 09:45:36+08:00,2025-08-29 09:49:22+08:00,4466.85,4458.15,447.0,3888.9000000003252,short,87d823b2-675d-4501-bb36-b40eef344b7c,28bb7706-f2ce-480e-b606-fc5073ce4153,
|
||||
2025-08-29 10:13:26+08:00,2025-08-29 10:18:05+08:00,4470.7,4473.08,,4.760000000000218,long,6d7fe294-0553-46ac-9179-e22446e727dc,b50fd6a4-865b-4cd4-8b83-015dde307912,2.0
|
||||
2025-08-29 10:13:27+08:00,2025-08-29 10:18:05+08:00,4470.71,4473.08,,1054.6499999999514,long,3d441a61-f852-4d4d-88d9-a2c4494e3a5f,b50fd6a4-865b-4cd4-8b83-015dde307912,445.0
|
||||
2025-08-29 10:21:04+08:00,2025-08-29 10:25:10+08:00,4479.68,4483.07,,1511.9399999997404,long,9ac71ec9-58a8-43af-83ca-cac772d44dce,7db1d0e0-cdda-45a4-af09-9ec2f6a830a5,446.0
|
||||
2025-08-29 11:08:08+08:00,2025-08-29 11:10:24+08:00,4477.82,4481.35,,1574.380000000292,long,4c447405-283c-40f7-bc05-42ae2aeaaaeb,de3c12d5-ba88-4b3d-8e8a-2158228465cb,446.0
|
||||
2025-08-29 11:25:26+08:00,2025-08-29 11:31:14+08:00,4478.16,4482.39,,1886.580000000211,long,877eb021-8a47-4d55-8efd-789fde87699c,33addd17-0151-418d-b11e-d68a26c179e6,446.0
|
||||
2025-08-29 11:35:59+08:00,2025-08-29 11:43:49+08:00,4484.05,4494.74,,4767.7399999998215,long,913408fa-2541-48c8-a8a0-d352b8b74a93,a7fd376e-dd1c-4350-8c58-1913d130902c,446.0
|
||||
2025-08-29 11:55:41+08:00,2025-08-29 12:00:52+08:00,4498.11,4489.03,444.0,4031.5199999999677,short,6450e48b-f901-4907-b065-18e073f86534,7cfe2e33-9b17-4785-9e45-dd5654c483c2,
|
||||
2025-08-29 12:54:59+08:00,2025-08-29 12:58:10+08:00,4477.15,4471.43,670.0,3832.3999999995613,short,13bb04ff-529a-48f4-a062-bdbda01abe9a,01a6ebd4-6c63-4e58-9f52-4317a83a6c27,
|
||||
2025-08-29 13:05:31+08:00,2025-08-29 13:06:05+08:00,4466.57,4472.34,671.0,-3871.670000000293,short,5c439227-9432-48c1-8c70-184e65586599,398e954e-0534-4f4c-bdda-5d14d858afae,
|
||||
2025-08-29 13:17:39+08:00,2025-08-29 13:19:35+08:00,4461.42,4468.3,448.0,-3082.240000000049,short,d770f2c0-6aed-4bc2-bee2-f98209775454,14ce65ce-8af2-4c07-8441-bdeea724197e,
|
||||
2025-08-29 13:59:25+08:00,2025-08-29 14:01:39+08:00,4476.01,4484.33,,382.7199999999866,long,1f80cf00-8910-42af-a7ac-dd2199579d04,96e160f6-0a35-4ac4-9f3e-295065a56092,46.0
|
||||
2025-08-29 13:59:25+08:00,2025-08-29 14:01:39+08:00,4476.01,4484.33,,3327.9999999998836,long,1f80cf00-8910-42af-a7ac-dd2199579d04,db2ea541-8ece-4876-baa0-f803629d4675,400.0
|
||||
2025-08-29 14:15:38+08:00,2025-08-29 14:18:12+08:00,4457.41,4451.26,448.0,2755.199999999837,short,2616d754-2ae8-43c0-a382-46901e0dba21,913e0886-23e4-467d-932d-79e960df2947,
|
||||
2025-08-29 14:37:31+08:00,2025-08-29 14:39:31+08:00,4451.44,4451.34,,-44.89999999975498,long,9f225f1e-cdc5-42cc-8c22-6d39f68da6d6,db5c616d-faf7-4183-8e46-b95f09dbad99,449.0
|
||||
2025-08-29 14:41:21+08:00,2025-08-29 14:44:17+08:00,4451.53,4455.7,,1872.3300000000327,long,5ba24885-5747-405a-8fa9-6a9749dbe6b4,f7e02df2-857b-4e3a-a6aa-c0d7362cb5bb,449.0
|
||||
2025-08-29 15:01:14+08:00,2025-08-29 15:02:16+08:00,4448.83,4428.34,449.0,9200.009999999902,short,a605bd22-e12e-44a4-9e1c-87c145bc5aa4,365ff72f-891a-4314-a005-d320d2074d5f,
|
||||
2025-08-29 15:12:00+08:00,2025-08-29 15:15:04+08:00,4412.37,4393.89,453.0,8371.439999999802,short,03169227-80cc-4c41-89e5-7cbc59174c90,7939f969-7566-453d-a718-dfa6b96ee987,
|
||||
2025-08-29 15:44:10+08:00,2025-08-29 15:51:01+08:00,4387.22,4391.19,683.0,-2711.5099999995527,short,e2151c19-3f56-4239-8368-77af88a0c2c1,f90b5024-cd5b-44d5-a140-4d6253d49d6a,
|
||||
2025-08-29 15:57:12+08:00,2025-08-29 16:00:46+08:00,4383.78,4387.09,400.0,-1324.00000000016,short,f2affaff-2beb-481f-b346-c9453422f536,70cc7f5d-b472-40a1-b00b-7e4b032722fc,
|
||||
2025-08-29 15:57:12+08:00,2025-08-29 16:00:46+08:00,4383.78,4387.09,284.0,-940.0400000001137,short,f2affaff-2beb-481f-b346-c9453422f536,676c76cd-7afa-40db-a0a7-89004bb9387b,
|
||||
2025-08-29 16:08:15+08:00,2025-08-29 16:11:24+08:00,4386.41,4376.76,383.0,3695.9499999998607,short,da4365c6-5bed-4bff-824c-580b3be527bc,0fdf84b4-d9bd-419e-8fd0-037e8631eefd,
|
||||
2025-08-29 16:08:15+08:00,2025-08-29 16:11:24+08:00,4386.5,4376.76,300.0,2921.9999999999345,short,940d2d22-8103-4bdd-8eaa-ade1277111da,0fdf84b4-d9bd-419e-8fd0-037e8631eefd,
|
||||
2025-08-29 16:17:21+08:00,2025-08-29 16:17:29+08:00,4364.59,4365.41,187.0,-153.33999999994558,short,89b93201-dfd2-408c-a5de-b6ecd72424cd,db922d70-7a90-4cf4-9a17-a598fe213f8b,
|
||||
2025-08-29 16:17:21+08:00,2025-08-29 16:17:29+08:00,4364.59,4365.41,400.0,-327.9999999998836,short,89b93201-dfd2-408c-a5de-b6ecd72424cd,574a0e60-b26e-46bf-a696-cae5868b7af2,
|
||||
2025-08-29 16:17:21+08:00,2025-08-29 16:17:29+08:00,4365.0,4365.41,100.0,-40.99999999998545,short,e6be51d9-e746-45dd-8522-877cf7eafd33,574a0e60-b26e-46bf-a696-cae5868b7af2,
|
||||
2025-08-29 16:20:18+08:00,2025-08-29 16:20:36+08:00,4359.92,4362.29,688.0,-1630.559999999925,short,d302895c-7318-4851-838d-396e7100fc09,719a6eff-a759-4947-8f0a-45f2ac94dfc4,
|
||||
2025-08-29 16:29:12+08:00,2025-08-29 16:33:21+08:00,4355.55,4362.63,,3731.1599999999617,long,f587dcce-e557-4b34-a55f-bf449234faca,e833feb6-8ba7-4fa5-adea-678f690e16b3,527.0
|
||||
2025-08-29 16:29:12+08:00,2025-08-29 16:33:21+08:00,4355.55,4362.63,,1139.8799999999883,long,22c48502-01b1-4efd-9809-3bc8487b0a9b,e833feb6-8ba7-4fa5-adea-678f690e16b3,161.0
|
||||
2025-08-29 16:37:39+08:00,2025-08-29 16:40:08+08:00,4356.21,4352.88,688.0,2291.03999999995,short,b209c4ae-8c33-4ba5-ad48-7559d1eafc6a,f7b8931f-1568-4387-a132-8ac6f7f34891,
|
||||
2025-08-29 16:40:40+08:00,2025-08-29 16:41:12+08:00,4348.53,4353.19,140.0,-652.3999999999796,short,9f88809c-8f0c-4dd4-b83c-552c6b78da3a,b2c3aa90-dcc1-469d-bf31-6c74b8e4099b,
|
||||
2025-08-29 16:40:40+08:00,2025-08-29 16:41:12+08:00,4348.53,4353.19,549.0,-2558.33999999992,short,3efb68fb-7389-44fd-b48a-d749af57e70f,b2c3aa90-dcc1-469d-bf31-6c74b8e4099b,
|
||||
2025-08-29 18:24:56+08:00,2025-08-29 18:28:40+08:00,4354.11,4357.4,,1644.9999999999818,long,5b3555c7-8c22-45ad-a4b5-ef6b586335d5,88db631a-7f3b-4b85-9887-94f906259583,500.0
|
||||
2025-08-29 18:24:56+08:00,2025-08-29 18:28:40+08:00,4354.11,4357.4,,621.8099999999931,long,f6c1a4f1-6aa0-4645-b991-503a320913be,6132d1a3-6d55-4517-b5ba-115198948b85,189.0
|
||||
2025-08-29 18:30:45+08:00,2025-08-29 18:49:35+08:00,4351.51,4343.72,189.0,1472.3099999999931,short,b67b7a3a-cf62-4731-bc9c-1d333f42e639,2d4f2adb-d806-4f37-8ff1-a46eddccc6b1,
|
||||
2025-08-29 18:30:45+08:00,2025-08-29 18:49:35+08:00,4351.51,4343.72,500.0,3894.999999999982,short,0acabab2-2ab4-4921-82a0-df806d4eb40d,2d4f2adb-d806-4f37-8ff1-a46eddccc6b1,
|
||||
2025-08-29 18:58:56+08:00,2025-08-29 19:02:25+08:00,4345.89,4340.99,,-3381.0000000003765,long,b525febe-c7eb-4db9-a4f8-4a09406c9f7d,51ddc961-238a-4c9d-b985-e6ad6ef2aac7,690.0
|
||||
2025-08-29 19:14:13+08:00,2025-08-29 19:18:33+08:00,4351.49,4346.64,,-3341.649999999624,long,da8dccbd-f79c-476b-9471-d28b719da66f,cfbfe97f-e085-41db-8679-58574bd165fd,689.0
|
||||
2025-08-29 19:49:41+08:00,2025-08-29 19:51:55+08:00,4345.68,4340.66,,-3463.800000000301,long,98c03fb1-dca8-43ef-9ca7-0fe6d842946b,e0c7b8be-c842-412e-bb4f-e43b51fa53bb,690.0
|
||||
2025-08-29 20:22:08+08:00,2025-08-29 20:30:35+08:00,4365.85,4411.23,,31176.059999999452,long,7ab49fe4-9bd7-4368-bb45-ffe9970e4999,c3d6e447-288e-48d1-8977-ccceda6a2e41,687.0
|
||||
2025-08-29 20:31:05+08:00,2025-08-29 20:31:21+08:00,4439.0,4448.35,,149.60000000000582,long,76a58764-fd12-4fb4-8ee9-407aa2213a12,e3f58dbc-6a93-4c16-9dc3-7e351493f00f,16.0
|
||||
2025-08-29 20:31:05+08:00,2025-08-29 20:31:21+08:00,4439.0,4448.35,,6161.65000000024,long,cdd4a741-4195-4fac-9896-35ee5c09a7b9,e3f58dbc-6a93-4c16-9dc3-7e351493f00f,659.0
|
||||
2025-08-29 20:31:46+08:00,2025-08-29 20:33:05+08:00,4409.9,4406.3,,-647.9999999999018,long,a47f68a7-9565-4ea7-b3ab-770d376ffcea,46e071a8-871b-4373-92a5-8cd6cd46cc56,180.0
|
||||
2025-08-29 20:31:46+08:00,2025-08-29 20:33:05+08:00,4409.9,4406.3,,-1799.9999999997272,long,a47f68a7-9565-4ea7-b3ab-770d376ffcea,c67209e2-0598-443d-85fa-752bdae6059b,500.0
|
||||
2025-08-29 20:43:25+08:00,2025-08-29 20:44:07+08:00,4382.62,4386.13,684.0,-2400.8400000001493,short,af3305f0-ec39-48fe-bf71-ac9acb281b25,7556c209-5046-4e8b-8682-99dc36803a62,
|
||||
2025-08-29 20:45:44+08:00,2025-08-29 20:49:11+08:00,4389.13,4407.06,,1828.8600000000297,long,a19435e4-fbf9-4217-bc5d-d7fc07d9818f,cc0577e9-e4b6-45fd-bf5a-47ccf6aec320,102.0
|
||||
2025-08-29 20:45:44+08:00,2025-08-29 20:49:11+08:00,4389.13,4407.06,,10417.33000000017,long,a19435e4-fbf9-4217-bc5d-d7fc07d9818f,4fa96e6c-6692-426b-beca-c138c8505a82,581.0
|
||||
2025-08-29 20:49:46+08:00,2025-08-29 20:53:14+08:00,4406.49,4406.02,500.0,234.99999999967258,short,d11f3a55-a1d8-41ff-a979-778c232459c9,b2de5ff9-9281-4c2e-86bd-a071a5e1e864,
|
||||
2025-08-29 20:49:46+08:00,2025-08-29 20:53:14+08:00,4406.49,4406.02,180.0,84.59999999988213,short,d11f3a55-a1d8-41ff-a979-778c232459c9,a4e3c4e5-4280-417c-8018-8386a63c318a,
|
||||
2025-08-29 21:00:24+08:00,2025-08-29 21:00:33+08:00,4408.35,4405.11,,-2203.20000000047,long,26e94954-9b31-48a9-9b2b-cb08da0f8e3e,14772435-8e33-4ee5-a87c-81342a7f8449,680.0
|
||||
2025-08-29 21:01:47+08:00,2025-08-29 21:06:54+08:00,4406.15,4391.41,39.0,574.8599999999915,short,8665f24a-3e13-4d3f-ad28-a702c79c73ed,7addab21-fcf3-4921-97ab-d77462067550,
|
||||
2025-08-29 21:01:47+08:00,2025-08-29 21:06:54+08:00,4406.15,4391.41,141.0,2078.339999999969,short,0170663d-0cf1-43a4-8595-4511c59b3107,7addab21-fcf3-4921-97ab-d77462067550,
|
||||
2025-08-29 21:01:47+08:00,2025-08-29 21:06:54+08:00,4406.15,4391.41,500.0,7369.999999999891,short,0170663d-0cf1-43a4-8595-4511c59b3107,3b754022-7c7c-4b14-8251-c2da4478df27,
|
||||
2025-08-29 21:10:41+08:00,2025-08-29 21:11:27+08:00,4400.55,4392.88,,-107.38000000000102,long,37e79409-f3ed-4e62-811c-6cc437396a41,f3a43d61-4d64-4d82-aada-31b38d441baa,14.0
|
||||
2025-08-29 21:10:41+08:00,2025-08-29 21:11:27+08:00,4400.55,4392.79,,-519.9200000000146,long,37e79409-f3ed-4e62-811c-6cc437396a41,86bfa728-af41-4e41-9e87-d6ef88b1a063,67.0
|
||||
2025-08-29 21:10:41+08:00,2025-08-29 21:11:27+08:00,4400.55,4392.88,,-4602.000000000044,long,37e79409-f3ed-4e62-811c-6cc437396a41,af01f0d0-8844-4267-8476-4000d0b2dd8b,600.0
|
||||
2025-08-29 21:30:11+08:00,2025-08-29 21:34:27+08:00,4386.05,4373.6,72.0,896.3999999999869,short,fecb81e1-3b36-4ae6-91ad-b6a2616ee608,aee7c322-ba5d-4b89-8b93-1c50ce5be75b,
|
||||
2025-08-29 21:30:11+08:00,2025-08-29 21:34:27+08:00,4386.05,4372.44,211.0,2871.710000000123,short,fecb81e1-3b36-4ae6-91ad-b6a2616ee608,e20951ed-ff4e-4a06-8bb5-8112a72e38bf,
|
||||
2025-08-29 21:30:11+08:00,2025-08-29 21:34:27+08:00,4386.05,4372.44,99.0,1347.3900000000576,short,fecb81e1-3b36-4ae6-91ad-b6a2616ee608,16329965-69b4-42a4-8145-79d1a0d2e92b,
|
||||
2025-08-29 21:30:11+08:00,2025-08-29 21:34:27+08:00,4386.29,4372.44,101.0,1398.8500000000367,short,f1e4b54c-d392-4dc4-8f73-40a8733b85fe,16329965-69b4-42a4-8145-79d1a0d2e92b,
|
||||
2025-08-29 21:30:11+08:00,2025-08-29 21:34:27+08:00,4386.29,4372.44,200.0,2770.0000000000728,short,f1e4b54c-d392-4dc4-8f73-40a8733b85fe,1b66a876-041a-480e-a9d6-428a3e301ec4,
|
||||
2025-08-30 08:15:30+08:00,2025-08-30 08:25:24+08:00,4343.62,4355.47,,4740.0000000001455,long,fb636839-9fb2-4e2f-bdb0-14bd57ade58a,6b8d4b73-132c-4c86-92f9-2a5df18943fd,400.0
|
||||
2025-08-30 08:15:30+08:00,2025-08-30 08:25:24+08:00,4343.62,4355.47,,3436.5000000001055,long,fb636839-9fb2-4e2f-bdb0-14bd57ade58a,a744bd39-3c81-4cb1-a153-60ed71d6748f,290.0
|
||||
2025-08-30 08:32:12+08:00,2025-08-30 08:35:26+08:00,4342.48,4337.98,,-288.0,long,e6185c1c-debd-4a9c-928c-edd28b360578,401ccb35-f82c-4df2-9a29-f9cd57481767,64.0
|
||||
2025-08-30 08:32:12+08:00,2025-08-30 08:35:26+08:00,4342.48,4335.64,,-3850.91999999957,long,e6185c1c-debd-4a9c-928c-edd28b360578,69d6c400-d534-47b6-b2a8-8da09b2986aa,563.0
|
||||
2025-08-30 08:32:12+08:00,2025-08-30 08:35:26+08:00,4342.48,4335.73,,-425.25,long,e6185c1c-debd-4a9c-928c-edd28b360578,4aa4a001-f8b0-4aea-93c3-546c71b81ee0,63.0
|
||||
2025-08-30 08:37:25+08:00,2025-08-30 08:41:59+08:00,4336.85,4332.05,691.0,3316.8000000001257,short,5cc6dd95-ec96-4864-a085-95b875673f0f,07f9b455-a026-41df-95c6-f9cf37f23a90,
|
||||
2025-08-30 09:26:28+08:00,2025-08-30 09:29:48+08:00,4326.54,4324.64,293.0,556.6999999998934,short,ce7db548-7de1-448e-92c9-25139a5f23a4,70062cb1-9074-49a9-8e7f-b5b7bae639d4,
|
||||
2025-08-30 09:26:28+08:00,2025-08-30 09:29:48+08:00,4326.54,4323.46,400.0,1231.999999999971,short,ce7db548-7de1-448e-92c9-25139a5f23a4,6145a0a3-248c-4822-8e8c-5efb6daf7d24,
|
||||
2025-08-30 09:51:41+08:00,2025-08-30 09:55:09+08:00,4312.57,4289.19,177.0,4138.260000000019,short,4b786a41-410d-4c93-b1c6-30b118f9e7f2,f78be76a-092e-4e0e-b8c3-26867d78c1de,
|
||||
2025-08-30 09:51:41+08:00,2025-08-30 09:55:09+08:00,4312.57,4289.19,518.0,12110.840000000057,short,96fbc46a-1dd3-4fc6-9e14-4a032e9464bf,f78be76a-092e-4e0e-b8c3-26867d78c1de,
|
||||
2025-08-30 09:55:49+08:00,2025-08-30 09:55:50+08:00,4282.58,4282.19,,-273.0000000002292,long,201dbee0-8f0d-427d-95d5-ef84e08a8aab,37ff5d0c-e10e-480c-91ca-0506e61330b6,700.0
|
||||
2025-08-30 09:58:19+08:00,2025-08-30 09:58:59+08:00,4281.59,4280.23,,-952.0000000004075,long,0e651112-db3b-402d-a268-1ea200cf8729,65b07548-3473-4570-be22-0172c49bdb94,700.0
|
||||
2025-08-30 10:00:09+08:00,2025-08-30 10:00:52+08:00,4264.04,4268.35,703.0,-3029.9300000002813,short,d9703fdd-ea02-410d-8303-d1d13b94748f,17fca37e-fc89-48a9-92ed-671ab0b9088e,
|
||||
2025-08-30 10:01:23+08:00,2025-08-30 10:02:02+08:00,4268.54,4271.97,110.0,-377.300000000032,short,022ce461-b78f-4127-9157-8c34a5e9272e,89468587-89ce-49cc-a9e3-343e24cb8036,
|
||||
2025-08-30 10:01:23+08:00,2025-08-30 10:02:02+08:00,4268.54,4271.97,592.0,-2030.5600000001723,short,9bdcfab5-9b8e-447c-b4ac-a82724e2774d,89468587-89ce-49cc-a9e3-343e24cb8036,
|
||||
2025-08-30 10:02:55+08:00,2025-08-30 10:04:26+08:00,4275.14,4280.56,400.0,-2168.000000000029,short,1b938bf4-dc99-4d2f-9e0b-c2fa5092d19d,4e6c40d8-e7c7-45ac-800e-5f71611479f1,
|
||||
2025-08-30 10:02:55+08:00,2025-08-30 10:04:26+08:00,4275.14,4280.56,141.0,-764.2200000000103,short,1b938bf4-dc99-4d2f-9e0b-c2fa5092d19d,864390a3-33dd-4a47-a3c2-471b8a0104f1,
|
||||
2025-08-30 10:02:55+08:00,2025-08-30 10:04:26+08:00,4275.14,4280.56,160.0,-867.2000000000116,short,7eb3eaf4-30fa-495d-a74c-61d725625c42,864390a3-33dd-4a47-a3c2-471b8a0104f1,
|
||||
2025-08-30 10:11:35+08:00,2025-08-30 10:18:43+08:00,4296.88,4306.91,,2326.959999999941,long,b1a002d6-951f-4912-b8cf-19bfc98f61ba,743a1e0f-48f3-43fe-9b6a-47536fa12ce1,232.0
|
||||
2025-08-30 10:11:35+08:00,2025-08-30 10:18:43+08:00,4296.91,4306.91,,4660.0,long,94664c85-6be6-42d8-984a-f3293ed9750f,743a1e0f-48f3-43fe-9b6a-47536fa12ce1,466.0
|
||||
2025-08-30 10:21:54+08:00,2025-08-30 10:27:07+08:00,4307.47,4306.32,696.0,800.4000000003798,short,641e7574-db24-4c78-8782-566b13de1e39,817cbe73-8f5f-4c27-893c-ddfe59e1890d,
|
||||
2025-08-30 10:28:32+08:00,2025-08-30 10:35:18+08:00,4305.75,4311.46,,2855.000000000018,long,dee31b34-3e7f-4b74-8774-ccd70f3cb124,d37eec35-4b90-42c7-82e7-7a56e909d268,500.0
|
||||
2025-08-30 10:28:32+08:00,2025-08-30 10:35:18+08:00,4305.75,4311.25,,1078.0,long,dee31b34-3e7f-4b74-8774-ccd70f3cb124,9cbf242a-7e93-4a3c-b798-6e3f7a88025d,196.0
|
||||
2025-08-30 10:48:32+08:00,2025-08-30 10:57:53+08:00,4313.05,4303.81,695.0,6421.799999999848,short,0efcab97-a0b6-4cd6-ad7e-22911ea32fa2,57fa177a-12c5-4127-91c4-289668a191ae,
|
||||
2025-08-30 11:03:45+08:00,2025-08-30 11:10:25+08:00,4308.11,4322.99,,4404.480000000032,long,36e3b573-d4be-4d54-9af3-8190731448c9,1e9213a7-3443-4209-a26a-d3e0feeaf325,296.0
|
||||
2025-08-30 11:03:45+08:00,2025-08-30 11:10:25+08:00,4308.11,4322.99,,5952.000000000044,long,36e3b573-d4be-4d54-9af3-8190731448c9,a344d092-7b22-4230-8ba9-0702dd353041,400.0
|
||||
2025-08-30 11:11:04+08:00,2025-08-30 11:17:45+08:00,4323.24,4328.87,,3901.5900000000756,long,c2e861bb-6de2-4dc0-aed3-7a385c0f34b8,d786c18b-8199-4335-ad6b-6115f39e089f,693.0
|
||||
2025-08-30 11:18:36+08:00,2025-08-30 11:23:02+08:00,4333.76,4358.0,,9695.999999999913,long,3c5242f4-5844-46a7-8b84-a6c73fed27c7,d71ce99c-8d8e-4f15-be2e-85d21e853298,400.0
|
||||
2025-08-30 11:18:36+08:00,2025-08-30 11:23:02+08:00,4333.76,4358.0,,3248.1599999999708,long,3c5242f4-5844-46a7-8b84-a6c73fed27c7,fe3607bd-9257-42ce-a0b3-06d882cb74af,134.0
|
||||
2025-08-30 11:18:36+08:00,2025-08-30 11:23:02+08:00,4333.76,4358.0,,3829.9199999999655,long,ce9f82f0-70a9-44b1-9175-4ee31dbc8fd6,fe3607bd-9257-42ce-a0b3-06d882cb74af,158.0
|
||||
2025-08-30 11:52:34+08:00,2025-08-30 11:57:42+08:00,4352.62,4352.32,,-20.70000000001255,long,a7a38452-b2e4-46c1-a14c-d85c0a79b250,078499df-67ff-4bdd-acaf-4e4fcf4457c0,69.0
|
||||
2025-08-30 11:52:34+08:00,2025-08-30 11:57:42+08:00,4352.62,4352.32,,-186.00000000011278,long,76c6328b-ae56-4313-9b73-3bc43f644ef4,078499df-67ff-4bdd-acaf-4e4fcf4457c0,620.0
|
||||
2025-08-30 12:01:25+08:00,2025-08-30 12:07:56+08:00,4351.41,4357.44,500.0,-3014.9999999998727,short,2228c2d3-6d7b-4835-8065-42ce6ad3a5af,3544a077-b511-45ef-b088-c380928048ee,
|
||||
2025-08-30 12:01:25+08:00,2025-08-30 12:07:56+08:00,4351.7,4357.44,121.0,-694.5399999999736,short,e33d8985-328c-4a44-829e-b8ebf6127582,3544a077-b511-45ef-b088-c380928048ee,
|
||||
2025-08-30 12:01:25+08:00,2025-08-30 12:07:56+08:00,4351.41,4357.44,527.0,-3177.809999999866,short,6f169ccd-3d5a-4e35-9653-7efd0a1580dc,3544a077-b511-45ef-b088-c380928048ee,
|
||||
2025-08-30 13:01:32+08:00,2025-08-30 13:07:24+08:00,4353.26,4358.47,577.0,-3006.170000000021,short,27412351-b7db-41d2-941e-014723b46c1a,f65cc5f5-8a78-4a59-bb58-cf0af4df8448,
|
||||
2025-08-30 13:01:33+08:00,2025-08-30 13:07:24+08:00,4353.26,4358.47,298.0,-1552.5800000000108,short,cab58243-ce8d-4149-b279-3417eac36fcd,f65cc5f5-8a78-4a59-bb58-cf0af4df8448,
|
||||
2025-08-30 13:01:33+08:00,2025-08-30 13:07:24+08:00,4353.26,4359.1,273.0,-1594.3200000000397,short,cab58243-ce8d-4149-b279-3417eac36fcd,c28367f5-fcb9-4345-82f2-c56451e2e462,
|
||||
2025-08-30 13:20:13+08:00,2025-08-30 13:32:38+08:00,4373.03,4377.0,1143.0,-4537.710000000291,short,0f73205b-a08a-4fad-9e77-f5d2fbbf1aee,6d5e0a1c-53e6-4fac-a76a-77cb3341a976,
|
||||
2025-08-30 13:33:46+08:00,2025-08-30 13:42:14+08:00,4385.31,4391.97,,7592.399999999834,long,11f0898c-7cba-49da-91d9-1fc6e9b57ab2,e7744350-f030-4566-bdeb-8a76060f8066,1140.0
|
||||
2025-08-30 13:48:01+08:00,2025-08-30 13:56:36+08:00,4395.74,4382.93,1137.0,14564.96999999942,short,d00ac585-7cde-477d-af47-a5889aa5ef70,e99ef7df-85e7-46b4-bc8a-4a6f2fc811fe,
|
||||
2025-08-30 13:58:04+08:00,2025-08-30 14:03:08+08:00,4381.43,4385.01,1141.0,-4084.779999999917,short,76216a51-657b-4659-86cb-2295aa642dac,8a9ab27e-38f2-4ec3-bbaf-642afd20a6e5,
|
||||
2025-08-30 14:10:44+08:00,2025-08-30 14:15:13+08:00,4383.15,4377.63,601.0,3317.5199999997158,short,587020d7-a3eb-4e44-b6c7-5c95b8c5451a,8ecfa693-c528-405b-8db4-0b02ccc7b9b3,
|
||||
2025-08-30 14:10:44+08:00,2025-08-30 14:15:13+08:00,4383.15,4377.63,500.0,2759.9999999997635,short,587020d7-a3eb-4e44-b6c7-5c95b8c5451a,babe1585-607c-4004-9aa9-19e1f5374ac1,
|
||||
2025-08-30 14:10:44+08:00,2025-08-30 14:15:13+08:00,4383.15,4377.63,39.0,215.27999999998156,short,587020d7-a3eb-4e44-b6c7-5c95b8c5451a,ab4e9d81-4133-42b0-9460-9a2efd36ec0b,
|
||||
2025-08-30 14:26:36+08:00,2025-08-30 14:33:14+08:00,4384.3,4391.39,,8082.600000000166,long,f703a5cc-3428-4796-a288-bfa6442800cb,a4e60efd-9afc-48b1-8850-6054e1330780,1140.0
|
||||
2025-08-30 14:45:13+08:00,2025-08-30 14:52:41+08:00,4395.22,4390.35,1137.0,5537.189999999876,short,b1f16517-7b15-43a4-a3e7-8f0b60f9f732,0445e9e3-c80c-4beb-a121-18b6094f52eb,
|
||||
2025-08-30 15:20:15+08:00,2025-08-30 15:40:40+08:00,4392.17,4398.92,52.0,-351.0,short,504ad303-3016-470e-9deb-a717d25a761c,9a648f36-ef00-45a4-8e56-868ab74acce6,
|
||||
2025-08-30 15:20:15+08:00,2025-08-30 15:40:40+08:00,4392.17,4398.92,500.0,-3375.0,short,59f674e3-aff1-4b79-b8b0-c650b299d27d,9a648f36-ef00-45a4-8e56-868ab74acce6,
|
||||
2025-08-30 15:20:15+08:00,2025-08-30 15:40:40+08:00,4392.17,4398.92,586.0,-3955.5,short,77548f6a-59ae-4ac2-880c-ea1f5679f507,9a648f36-ef00-45a4-8e56-868ab74acce6,
|
||||
2025-08-30 15:46:04+08:00,2025-08-30 15:50:09+08:00,4408.51,4402.78,,-6497.820000000536,long,76eb4bce-ff36-4d4c-b97a-cda6d9f7b544,63f7184c-7211-417f-b594-e39436d14940,1134.0
|
||||
2025-08-30 16:11:35+08:00,2025-08-30 16:32:13+08:00,4393.35,4389.12,400.0,1692.0000000001892,short,be9ff2ce-0d9e-4184-8d43-c49b33c71c95,754db465-b458-43a3-b2b5-3fb4f9fc2aaf,
|
||||
2025-08-30 16:11:35+08:00,2025-08-30 16:32:13+08:00,4393.35,4389.12,500.0,2115.0000000002365,short,cdf388db-68a2-47ea-ba94-431f02dadeac,754db465-b458-43a3-b2b5-3fb4f9fc2aaf,
|
||||
2025-08-30 16:11:35+08:00,2025-08-30 16:32:13+08:00,4393.35,4389.12,237.0,1002.5100000001121,short,e6f8d379-c20f-4a9d-90a4-7a5904bff1f9,754db465-b458-43a3-b2b5-3fb4f9fc2aaf,
|
||||
2025-08-30 17:05:10+08:00,2025-08-30 17:11:26+08:00,4390.54,4387.01,133.0,469.48999999996613,short,d4db2208-ba63-42c7-af51-9cec92bfe6f1,46858333-121c-4020-9567-bc44d63dec01,
|
||||
2025-08-30 17:05:10+08:00,2025-08-30 17:11:26+08:00,4390.54,4387.01,190.0,670.6999999999516,short,d4db2208-ba63-42c7-af51-9cec92bfe6f1,4d19376a-cfb7-4da9-8859-982e073624d1,
|
||||
2025-08-30 17:05:10+08:00,2025-08-30 17:11:26+08:00,4390.54,4387.01,310.0,1094.299999999921,short,00dda8f5-7156-4d25-9ea0-29e5fb2a4aca,4d19376a-cfb7-4da9-8859-982e073624d1,
|
||||
2025-08-30 17:05:10+08:00,2025-08-30 17:11:26+08:00,4390.54,4387.01,505.0,1782.6499999998714,short,00dda8f5-7156-4d25-9ea0-29e5fb2a4aca,e28b9838-7c48-4c78-a369-8259844cc3f4,
|
||||
2025-08-30 17:18:39+08:00,2025-08-30 17:28:37+08:00,4389.73,4385.08,,-5296.349999999586,long,2789c169-8a5d-47da-a6f9-6800e72ec26e,cd9481fb-33de-4e75-beb8-119ace92b5f6,1139.0
|
||||
2025-08-30 17:31:17+08:00,2025-08-30 17:37:08+08:00,4379.77,4384.13,1141.0,-4974.759999999626,short,434fc13a-925d-4abd-8677-0cd2b1a412f0,1e63ca1f-7f77-4ed9-a885-159f5dac641e,
|
||||
2025-08-30 17:44:10+08:00,2025-08-30 17:48:40+08:00,4384.13,4388.17,1140.0,-4605.5999999999585,short,288b4dc7-affd-4b36-897a-e02a024dbae0,70c0e310-219d-45d2-a91d-6b685af4a065,
|
||||
2025-08-30 17:50:34+08:00,2025-08-30 17:54:02+08:00,4389.94,4393.0,,3482.2800000004554,long,b78acf6a-d3b3-44c2-b484-ad95dfedd167,9473c228-7657-44cd-9038-38aeb833c3ab,1138.0
|
||||
2025-08-30 18:05:09+08:00,2025-08-30 18:07:30+08:00,4392.24,4394.52,,2594.640000000745,long,44cd4f02-5250-47d0-8e71-a66f856405ba,81abc1fa-1789-4f69-aaaa-2da1f3f34186,1138.0
|
||||
2025-08-31 10:28:55+08:00,2025-08-31 10:34:47+08:00,4450.3,4446.25,,-1320.3000000000593,long,0fe00795-d7d9-4586-b864-e445017c7f9d,716e265c-d39a-46b3-92e2-99f5d8a6b45f,326.0
|
||||
2025-08-31 10:28:55+08:00,2025-08-31 10:34:47+08:00,4450.3,4446.25,,-2316.600000000104,long,0fe00795-d7d9-4586-b864-e445017c7f9d,e7b360d6-da94-4cb7-b8c7-0b10db53a03b,572.0
|
||||
2025-08-31 11:08:10+08:00,2025-08-31 11:12:01+08:00,4454.6,4458.68,897.0,-3659.7599999999347,short,c31d66e2-a558-4b86-a520-3a9eb9d55346,f737efdc-5818-4bc5-b3db-1da76e7caf71,
|
||||
2025-08-31 11:24:10+08:00,2025-08-31 11:27:33+08:00,4457.8,4453.59,,-2138.6800000000185,long,38413547-9d48-43fc-a2be-12d2a8e0cd9f,ce5c15c0-d938-4b48-ab08-807531847f2e,508.0
|
||||
2025-08-31 11:24:10+08:00,2025-08-31 11:27:33+08:00,4457.8,4453.59,,-1637.6900000000142,long,816792ec-d409-4688-b7fd-2ee3e3f4491e,ce5c15c0-d938-4b48-ab08-807531847f2e,389.0
|
||||
2025-08-31 11:49:43+08:00,2025-08-31 11:53:50+08:00,4462.28,4469.74,,3730.000000000018,long,b1e2c3bf-db41-4762-b152-7a6a469c8cdb,52c185f1-3f74-42b6-afe5-6032df9dcb11,500.0
|
||||
2025-08-31 11:49:43+08:00,2025-08-31 11:53:50+08:00,4462.28,4469.74,,2954.1600000000144,long,b1e2c3bf-db41-4762-b152-7a6a469c8cdb,5fbbe739-8d3c-4aab-8333-7860bdca1d69,396.0
|
||||
2025-08-31 11:54:26+08:00,2025-08-31 12:00:20+08:00,4475.79,4483.53,,2267.819999999936,long,614c6489-c5a5-451d-a2a0-699e38d5a695,5da7f47f-33e3-45b8-9dfe-8232807978fc,293.0
|
||||
2025-08-31 11:54:26+08:00,2025-08-31 12:00:20+08:00,4475.79,4483.53,,1547.9999999999563,long,c443995b-f986-4a55-b6d9-59c43fa71fd2,5da7f47f-33e3-45b8-9dfe-8232807978fc,200.0
|
||||
2025-08-31 11:54:26+08:00,2025-08-31 12:00:20+08:00,4475.79,4483.53,,3095.9999999999127,long,c443995b-f986-4a55-b6d9-59c43fa71fd2,7d96c3c9-b227-48f3-b9fe-9a5c08871cfe,400.0
|
||||
2025-08-31 12:15:25+08:00,2025-08-31 12:25:03+08:00,4473.35,4467.05,894.0,5632.200000000163,short,d5064049-73a7-4a01-8d14-3d0ba056d572,b4537133-4056-4afc-80c2-7656e28cff88,
|
||||
2025-08-31 12:30:21+08:00,2025-08-31 12:33:51+08:00,4461.85,4465.99,500.0,-2069.999999999709,short,516d9754-0fe4-4f2e-9289-5827765f7c67,13f52cac-de10-4bba-93d5-8376ba2c5139,
|
||||
2025-08-31 12:30:21+08:00,2025-08-31 12:33:51+08:00,4461.85,4465.88,100.0,-402.99999999997453,short,516d9754-0fe4-4f2e-9289-5827765f7c67,a77196b2-cc47-482b-8fb0-a140d25da936,
|
||||
2025-08-31 12:30:21+08:00,2025-08-31 12:33:51+08:00,4461.85,4465.99,296.0,-1225.4399999998277,short,516d9754-0fe4-4f2e-9289-5827765f7c67,c529dbf2-2dd4-46aa-b5b6-dd0ed4f473e2,
|
||||
2025-08-31 12:42:50+08:00,2025-08-31 12:48:13+08:00,4464.2,4468.3,661.0,-2710.1000000002405,short,0f3ef65f-e754-4229-87e6-e0f102020ce9,bce7cdc5-2c37-4bda-9b2a-05c865dd69ba,
|
||||
2025-08-31 12:42:50+08:00,2025-08-31 12:48:13+08:00,4464.2,4468.3,235.0,-963.5000000000855,short,10e40e0b-a920-4e80-8d17-ea6f73d645e3,bce7cdc5-2c37-4bda-9b2a-05c865dd69ba,
|
||||
2025-08-31 13:31:35+08:00,2025-08-31 13:38:28+08:00,4453.99,4450.23,,-376.0000000000218,long,e65553ad-4738-4b5f-af08-c7373301aef8,ce3b836f-f3c8-4e27-bdd5-33b9e1ba2bc9,100.0
|
||||
2025-08-31 13:31:35+08:00,2025-08-31 13:38:28+08:00,4454.98,4450.23,,-2850.0,long,57f64aba-942f-4563-a3c7-9dfdfeaf98e4,ce3b836f-f3c8-4e27-bdd5-33b9e1ba2bc9,600.0
|
||||
2025-08-31 13:31:35+08:00,2025-08-31 13:38:28+08:00,4454.98,4450.23,,-940.5,long,597f272b-ce3c-45bf-99ee-4abe2f630972,ce3b836f-f3c8-4e27-bdd5-33b9e1ba2bc9,198.0
|
||||
2025-08-31 14:22:05+08:00,2025-08-31 14:23:54+08:00,4441.46,4446.28,275.0,-1325.49999999992,short,eafee2f8-8601-47c7-bfe0-2f0a35bd1411,3744b6fa-5971-44d1-885f-7c7f93da8e70,
|
||||
2025-08-31 14:22:05+08:00,2025-08-31 14:23:54+08:00,4442.21,4446.28,100.0,-406.9999999999709,short,702cd777-dc0b-4602-8c19-618520cf46f4,3744b6fa-5971-44d1-885f-7c7f93da8e70,
|
||||
2025-08-31 14:22:05+08:00,2025-08-31 14:23:54+08:00,4441.46,4446.28,525.0,-2530.499999999847,short,be46bffb-290a-477a-81d9-fd4e7c1d2f3c,3744b6fa-5971-44d1-885f-7c7f93da8e70,
|
||||
2025-08-31 14:33:49+08:00,2025-08-31 14:39:00+08:00,4439.81,4444.22,900.0,-3968.999999999869,short,f25e9b2a-1bb7-4bc5-852e-5c953f222f61,2d0c6c6c-070a-498b-ade7-0235a6d3e644,
|
||||
2025-08-31 14:40:14+08:00,2025-08-31 14:47:51+08:00,4444.0,4439.28,,-1939.9200000001047,long,46e5c712-8dff-423d-805d-d09da5e43571,9a2c96f9-3c09-4cca-8539-7251e96e99df,411.0
|
||||
2025-08-31 14:40:14+08:00,2025-08-31 14:47:51+08:00,4444.0,4439.13,,-1947.9999999999563,long,46e5c712-8dff-423d-805d-d09da5e43571,f92ff1ec-cfa2-4576-bcbb-978910e5200c,400.0
|
||||
2025-08-31 14:40:14+08:00,2025-08-31 14:47:51+08:00,4444.0,4439.13,,-433.4299999999903,long,46e5c712-8dff-423d-805d-d09da5e43571,5f572e4d-e1a7-45cf-bc1b-b55ce56f12b4,89.0
|
||||
2025-08-31 14:51:09+08:00,2025-08-31 14:57:17+08:00,4436.57,4435.16,450.0,634.4999999999345,short,3d5b8a22-edc9-4076-8d61-23f8ed561202,9c959838-3125-426e-abaf-d09fc309fda2,
|
||||
2025-08-31 15:30:46+08:00,2025-08-31 15:33:17+08:00,4450.96,4446.74,,-1894.7800000001143,long,45ada65c-b8e3-4316-9414-5faf1dcad582,57564f53-121e-47ac-8804-e448bb39c82c,449.0
|
||||
2025-08-31 16:05:31+08:00,2025-08-31 16:08:48+08:00,4461.21,4457.34,,-1733.759999999951,long,050937e7-f1e4-459e-982a-8d5263e718cb,ad5c9d31-f050-4981-9f51-7852dbf0df37,448.0
|
||||
2025-08-31 16:20:45+08:00,2025-08-31 16:24:22+08:00,4463.0,4462.93,,-3.35999999998603,long,e41030f3-095f-4e36-b465-e7f7d20b2972,83b7259d-91f7-485a-92ee-37c94790c7f4,48.0
|
||||
2025-08-31 16:20:45+08:00,2025-08-31 16:24:22+08:00,4463.0,4462.93,,-27.999999999883585,long,e41030f3-095f-4e36-b465-e7f7d20b2972,de316218-6bb2-48c0-b540-fc4babf5ce9b,400.0
|
||||
2025-08-31 17:24:37+08:00,2025-08-31 17:27:28+08:00,4473.73,4467.56,447.0,2757.989999999626,short,b8d42b12-b7c8-465d-a67b-56085ceae48f,74ed9f75-4d45-47a8-a636-5a1cbe5511ce,
|
||||
2025-08-31 17:48:57+08:00,2025-08-31 17:52:50+08:00,4463.45,4470.31,,3073.2800000002608,long,9b9c8688-3c74-48c3-b1bf-6bbc162b3edf,70a47e37-f8fb-440f-9ea6-50ffbf8055f6,448.0
|
||||
2025-08-31 17:59:19+08:00,2025-08-31 18:00:09+08:00,4461.24,4465.19,152.0,-600.3999999999724,short,28a00768-c1e1-42d0-b75e-f7b6b8608b60,9263141f-f5ac-4173-b3c2-b8e2f7ba0985,
|
||||
2025-08-31 17:59:19+08:00,2025-08-31 18:00:09+08:00,4461.24,4465.19,520.0,-2053.9999999999054,short,28a00768-c1e1-42d0-b75e-f7b6b8608b60,32d89f55-ae9c-49a6-8e2a-e47b5f4167ab,
|
||||
2025-08-31 18:07:59+08:00,2025-08-31 18:12:30+08:00,4459.1,4452.94,672.0,4139.520000000513,short,765d6b9a-0683-4dca-b76c-6e9a2ea65cd5,7f047d14-dac5-4fca-94ed-c0fdfec63578,
|
||||
2025-08-31 18:17:18+08:00,2025-08-31 18:29:14+08:00,4451.84,4455.42,,2409.339999999951,long,755ad988-d66e-4bc8-9907-b69112e3a11a,775a3323-bbd4-4e5c-ae56-d68e178eb05e,673.0
|
||||
2025-08-31 22:07:31+08:00,2025-08-31 22:13:15+08:00,4453.82,4450.0,1122.0,4286.0399999996735,short,c3c54d31-6a99-45ad-8139-546ac61bed34,ef4df9cc-3394-4d53-8fed-7e261a8b0c60,
|
||||
2025-08-31 22:14:11+08:00,2025-08-31 22:20:53+08:00,4453.63,4459.25,,2809.9999999999454,long,18042f61-552b-41bb-b228-8d8031ee3c14,ad7b6056-101b-49e4-9b9f-dea68880cd52,500.0
|
||||
2025-08-31 22:14:11+08:00,2025-08-31 22:20:53+08:00,4453.63,4459.25,,685.6399999999867,long,208d18b8-941b-4ce3-aa29-2f5fb074cc01,ad7b6056-101b-49e4-9b9f-dea68880cd52,122.0
|
||||
2025-08-31 22:14:11+08:00,2025-08-31 22:20:53+08:00,4453.63,4459.25,,2809.9999999999454,long,4e557687-e4c4-4f2d-85b6-364f6e5fd752,ad7b6056-101b-49e4-9b9f-dea68880cd52,500.0
|
||||
2025-08-31 23:05:00+08:00,2025-08-31 23:09:16+08:00,4473.11,4471.27,,-2055.2799999991466,long,41d06c42-ac03-418f-9451-6f5a5fdda71a,5cd65f94-490b-4bb2-bd42-d1a64942d201,1117.0
|
||||
2025-09-01 09:07:41+08:00,2025-09-01 09:11:05+08:00,4394.0,4383.39,400.0,4243.999999999869,short,092adf53-a461-4de7-8e04-9bfe1de8c387,b3e26049-21f9-495e-9a96-7498f06dcd82,
|
||||
2025-09-01 09:07:41+08:00,2025-09-01 09:11:05+08:00,4394.0,4383.39,55.0,583.549999999982,short,092adf53-a461-4de7-8e04-9bfe1de8c387,0614bd80-655c-49d4-b895-798f56f54803,
|
||||
2025-09-01 09:20:32+08:00,2025-09-01 09:25:17+08:00,4399.41,4396.45,,-1343.8400000000165,long,ef1554ec-bb75-4b5d-a3fd-03325d8ce248,11fb1482-9f6a-4b3f-aec4-e0e9f46ec4eb,454.0
|
||||
2025-09-01 09:31:04+08:00,2025-09-01 09:32:03+08:00,4409.02,4405.26,,-1703.2800000000989,long,8050d64e-1c53-40e1-89e7-68481d09e689,079730bc-d8ce-413a-a77f-a889bed73972,453.0
|
||||
2025-09-01 09:34:47+08:00,2025-09-01 09:37:04+08:00,4405.92,4417.68,,5327.280000000099,long,4f3b316a-37b6-43b9-a742-28760ce5267b,18f19970-4048-485b-b95a-00b3e804d0d9,453.0
|
||||
2025-09-01 10:11:29+08:00,2025-09-01 10:16:05+08:00,4400.12,4391.86,400.0,3304.0000000000873,short,9b7a45aa-4ed0-4fa2-9c77-ee71e8319ecf,6adc05fa-6727-4961-8ae9-ef7f248ce06f,
|
||||
2025-09-01 10:11:30+08:00,2025-09-01 10:16:05+08:00,4400.12,4391.86,54.0,446.0400000000118,short,a2b8e0df-1a70-4cbe-b513-734c169a45e5,6adc05fa-6727-4961-8ae9-ef7f248ce06f,
|
||||
2025-09-01 10:29:03+08:00,2025-09-01 10:30:26+08:00,4389.33,4386.21,,-2130.9599999999255,long,9295d00e-a050-472f-b008-426e40eb0f71,73e0f83a-583a-4ae6-8dc3-41c2f37bb58e,683.0
|
||||
2025-09-01 10:35:00+08:00,2025-09-01 10:36:12+08:00,4375.45,4379.38,685.0,-2692.0500000001994,short,8e5b463d-f4fe-47f3-b39b-ec3721c79e7a,2f96c834-2272-491f-8bc1-8cbe6c4ea452,
|
||||
2025-09-01 11:03:26+08:00,2025-09-01 11:05:42+08:00,4383.62,4391.35,,5287.3200000003235,long,ad2ab2b0-b070-4027-ba77-039ae801c30b,ab66e717-8f29-4ebe-80c7-165f36806e5f,684.0
|
||||
2025-09-01 11:12:18+08:00,2025-09-01 11:15:56+08:00,4382.88,4374.85,184.0,1477.5199999999531,short,60e70f9e-3ed0-402a-a33e-0d9104ac17b6,2c200cb7-b183-474a-b78f-bba9e592c8c0,
|
||||
2025-09-01 11:12:18+08:00,2025-09-01 11:15:56+08:00,4382.88,4374.85,500.0,4014.9999999998727,short,4f57713c-6a4c-42d4-aa6b-c03bae934835,2c200cb7-b183-474a-b78f-bba9e592c8c0,
|
||||
2025-09-01 11:34:38+08:00,2025-09-01 11:41:09+08:00,4370.18,4382.21,,3440.579999999927,long,7716be61-be89-4b30-8939-ff0c2c42b4e2,15dadfe0-75d8-4cc7-838d-931a1d21ccf8,286.0
|
||||
2025-09-01 11:34:38+08:00,2025-09-01 11:41:09+08:00,4370.18,4382.21,,4811.999999999898,long,7716be61-be89-4b30-8939-ff0c2c42b4e2,9ae0cd54-0bfb-4a24-8c7a-1a247e078eb8,400.0
|
||||
2025-09-01 11:54:59+08:00,2025-09-01 11:58:53+08:00,4396.0,4403.97,,79.70000000000255,long,b0876036-7e56-46cb-bddd-ccabb6519f7c,600628e2-049f-44c8-a905-46ea1135e601,10.0
|
||||
2025-09-01 11:54:59+08:00,2025-09-01 11:58:53+08:00,4396.47,4403.97,,1087.5,long,4bc131e8-70e0-4140-a2c8-b054e0507acd,600628e2-049f-44c8-a905-46ea1135e601,145.0
|
||||
2025-09-01 11:54:59+08:00,2025-09-01 11:58:53+08:00,4396.47,4403.97,,2662.5,long,4bc131e8-70e0-4140-a2c8-b054e0507acd,9d3eca96-2443-4c6e-b40e-1020bde26895,355.0
|
||||
2025-09-01 11:54:59+08:00,2025-09-01 11:58:53+08:00,4396.47,4403.97,,1290.0,long,86ab0723-57a8-4e31-9000-9b73f49ad308,9d3eca96-2443-4c6e-b40e-1020bde26895,172.0
|
||||
2025-09-01 12:19:34+08:00,2025-09-01 12:23:09+08:00,4389.68,4388.07,500.0,805.000000000291,short,2aec9027-7a84-45c2-83d3-8c5871d90106,95aa0191-4e59-46b0-b538-e0a3fd31a029,
|
||||
2025-09-01 12:19:34+08:00,2025-09-01 12:23:09+08:00,4389.68,4388.0,87.0,146.16000000002532,short,2aec9027-7a84-45c2-83d3-8c5871d90106,861ff638-8ec8-4a6a-bafa-74206b093040,
|
||||
2025-09-01 12:19:34+08:00,2025-09-01 12:23:09+08:00,4389.68,4388.07,52.0,83.72000000003027,short,2aec9027-7a84-45c2-83d3-8c5871d90106,6cff4ab5-d999-4dda-87e2-067c9adbd240,
|
||||
2025-09-01 12:19:34+08:00,2025-09-01 12:23:09+08:00,4389.68,4388.07,500.0,805.000000000291,short,2aec9027-7a84-45c2-83d3-8c5871d90106,4dc3eeb9-b54d-4820-bc73-2172e8e5fd66,
|
||||
2025-09-01 12:52:26+08:00,2025-09-01 12:56:26+08:00,4381.97,4385.85,,4427.0800000001245,long,a3ef0945-101d-4f17-9cda-711fe44e2cf1,ab314993-0d4e-4632-8bcd-66aaaef57066,1141.0
|
||||
2025-09-01 13:11:24+08:00,2025-09-01 13:12:28+08:00,4363.3,4368.36,475.0,-2403.499999999758,short,4d1d7cf6-26fe-44f0-be21-d10a9871ae1a,53f10353-21b1-41c1-8554-18c5bac4991a,
|
||||
2025-09-01 13:11:24+08:00,2025-09-01 13:12:28+08:00,4363.3,4368.36,400.0,-2023.9999999997963,short,63978423-878f-4bc0-8ac6-7526d1aa3b24,53f10353-21b1-41c1-8554-18c5bac4991a,
|
||||
2025-09-01 13:11:24+08:00,2025-09-01 13:12:28+08:00,4363.3,4368.36,500.0,-2529.9999999997453,short,9736d4ed-bfd4-4fb4-a2b9-7a335070a480,53f10353-21b1-41c1-8554-18c5bac4991a,
|
||||
2025-09-01 13:19:17+08:00,2025-09-01 13:20:04+08:00,4357.3,4364.42,1376.0,-9797.11999999985,short,9d7b259b-c1ec-4d54-83c5-e1588347d99f,a63b68c2-d467-4b4b-992a-175e562a59cd,
|
||||
2025-09-01 13:23:06+08:00,2025-09-01 13:33:39+08:00,4390.89,4384.71,,-1798.3800000000847,long,348b7ba0-df18-4d21-b652-1c54dc5b4635,2a709f71-d330-49c0-9759-3b65a140431c,291.0
|
||||
2025-09-01 13:23:06+08:00,2025-09-01 13:33:39+08:00,4390.89,4384.71,,-1854.0000000000873,long,c8c60f9e-a45b-4f5a-b000-a1a436757403,2a709f71-d330-49c0-9759-3b65a140431c,300.0
|
||||
2025-09-01 13:23:06+08:00,2025-09-01 13:33:39+08:00,4390.89,4384.71,,-1854.0000000000873,long,970891fc-d2ec-4c54-a196-7a450a88c968,2a709f71-d330-49c0-9759-3b65a140431c,300.0
|
||||
2025-09-01 13:23:06+08:00,2025-09-01 13:33:39+08:00,4390.01,4384.71,,-106.00000000000364,long,71cd000e-8c95-4f6f-a32f-1440db6e8c0d,2a709f71-d330-49c0-9759-3b65a140431c,20.0
|
||||
2025-09-01 13:49:13+08:00,2025-09-01 13:54:15+08:00,4396.35,4396.21,,-40.18000000009397,long,83a1a14a-a4e3-40f1-86bd-915987501b35,4c298f23-9a76-4a0f-8bc1-f8ed23f12ede,287.0
|
||||
2025-09-01 13:49:13+08:00,2025-09-01 13:54:15+08:00,4396.35,4396.21,,-28.000000000065484,long,83a1a14a-a4e3-40f1-86bd-915987501b35,46728c29-6645-408a-bba2-07f01cd5d06d,200.0
|
||||
2025-09-01 13:49:13+08:00,2025-09-01 13:54:15+08:00,4396.35,4396.21,,-1.8200000000042564,long,83a1a14a-a4e3-40f1-86bd-915987501b35,c4cb8611-b4dd-41fd-95c2-c9455c1237e6,13.0
|
||||
2025-09-01 13:49:13+08:00,2025-09-01 13:54:15+08:00,4396.35,4396.21,,-26.180000000061227,long,47230a4e-8ee9-4d68-aa51-01753afc623b,c4cb8611-b4dd-41fd-95c2-c9455c1237e6,187.0
|
||||
2025-09-01 13:49:13+08:00,2025-09-01 13:54:15+08:00,4396.35,4396.13,,-46.86000000005424,long,47230a4e-8ee9-4d68-aa51-01753afc623b,e06fcad9-5742-4c94-82eb-2f34884e3f4a,213.0
|
||||
2025-09-01 13:49:14+08:00,2025-09-01 13:54:15+08:00,4396.35,4396.13,,-1.980000000002292,long,1743d43c-c642-434b-940f-b6d33e6718b9,e06fcad9-5742-4c94-82eb-2f34884e3f4a,9.0
|
||||
2025-09-01 15:34:19+08:00,2025-09-01 15:35:08+08:00,4429.5,4426.01,,-1406.469999999912,long,e9dad14a-702d-4396-a259-38e6c3b62876,76c3e1c9-aeee-41bd-b8d4-37e8948af1c4,403.0
|
||||
2025-09-01 15:34:19+08:00,2025-09-01 15:35:08+08:00,4429.5,4426.01,,-1744.9999999998909,long,431b7e4b-b81e-4626-a6de-f5acd5a7cf20,76c3e1c9-aeee-41bd-b8d4-37e8948af1c4,500.0
|
||||
2025-09-01 15:36:02+08:00,2025-09-01 15:36:51+08:00,4424.4,4419.44,,-3868.8000000000284,long,d7ba8670-d0d7-4817-b30f-ed7d2ba4fd3c,74c07207-3acc-4f9c-b98b-ae54e4085d96,780.0
|
||||
2025-09-01 15:36:02+08:00,2025-09-01 15:36:51+08:00,4424.4,4420.0,,-545.5999999999549,long,d7ba8670-d0d7-4817-b30f-ed7d2ba4fd3c,a4bea0b9-d2b0-42bf-9e3b-371747ea4fa3,124.0
|
||||
2025-09-01 15:44:38+08:00,2025-09-01 15:47:21+08:00,4420.39,4426.94,,4427.799999999508,long,17564d45-1b57-4822-91dd-7bf3a162e7b1,131e056d-1779-4de8-bb12-1663936c3ffe,676.0
|
||||
2025-09-01 15:44:38+08:00,2025-09-01 15:47:21+08:00,4420.0,4426.94,,152.6799999999912,long,7e280213-31c8-4848-b164-57ce0f3a30c3,131e056d-1779-4de8-bb12-1663936c3ffe,22.0
|
||||
2025-09-01 15:44:38+08:00,2025-09-01 15:47:21+08:00,4420.39,4426.94,,1349.2999999998501,long,37898b4e-9a13-4bc0-a9e6-3fa908bd1e80,131e056d-1779-4de8-bb12-1663936c3ffe,206.0
|
||||
2025-09-01 15:51:28+08:00,2025-09-01 15:54:46+08:00,4434.86,4450.54,,6068.160000000113,long,a62d6355-e666-4b24-a9fe-1a8efd802cd4,c5b380bf-78c7-4dfd-a1b8-f212e0562e5d,387.0
|
||||
2025-09-01 15:51:28+08:00,2025-09-01 15:54:46+08:00,4434.86,4450.54,,8059.52000000015,long,9383482c-67d4-4a52-9c9a-bf106f8a66ed,c5b380bf-78c7-4dfd-a1b8-f212e0562e5d,514.0
|
||||
2025-09-01 15:59:35+08:00,2025-09-01 16:02:01+08:00,4454.21,4472.89,,16774.64000000026,long,b4194b4d-5b4a-4387-879f-b208a173d23e,0b3a10ad-d3bc-49e7-bc76-95d3f3f7b012,898.0
|
||||
2025-09-01 16:05:00+08:00,2025-09-01 16:05:43+08:00,4486.3,4479.29,,-6245.9100000001945,long,62035134-6e2c-4e78-a57e-90883d6fd125,e148f8b3-d2b0-49a8-9702-bf0ef6d70a11,891.0
|
||||
2025-09-01 16:06:22+08:00,2025-09-01 16:13:08+08:00,4478.53,4480.64,893.0,-1884.2300000005198,short,d901988b-b5b6-4a5e-a03c-83cedbdbbbda,dcc81b54-5253-4a49-a426-f28316341b68,
|
||||
2025-09-01 16:18:38+08:00,2025-09-01 16:19:03+08:00,4467.53,4470.68,895.0,-2819.2500000004884,short,2a066536-51d7-425d-ae9e-83613597c612,41ad454f-5790-480c-9f16-80b478841763,
|
||||
2025-09-01 16:20:05+08:00,2025-09-01 16:22:22+08:00,4473.83,4478.12,894.0,-3835.2599999999675,short,93df10c4-453d-405e-aa78-3870c5c5f78f,5aeedf17-a10d-4388-b16e-e18251d6e1e5,
|
||||
2025-09-01 16:23:14+08:00,2025-09-01 16:27:37+08:00,4475.22,4469.41,893.0,5188.330000000357,short,d9277a5f-844a-4afc-b784-0343d373a87e,7def0a70-6cc3-4880-9da3-16671ec6a296,
|
||||
2025-09-01 16:33:42+08:00,2025-09-01 16:41:25+08:00,4470.45,4469.46,894.0,885.0599999998049,short,9e7bf3df-d22c-413b-8b29-024b9c830f6b,3e80d0e3-3466-4e63-b75d-c86b67b6eb0b,
|
||||
2025-09-01 16:55:38+08:00,2025-09-01 17:04:46+08:00,4474.93,4476.7,893.0,-1580.6099999995777,short,aca7bfae-9112-4a7a-8a95-7b7661be7d13,20115f47-253a-435a-9b43-1cfcbdc9c12e,
|
||||
2025-09-01 17:10:25+08:00,2025-09-01 17:15:02+08:00,4472.69,4480.01,,6544.080000000553,long,44c66b7e-5da3-4773-b5a0-73b1cd1c13b9,d8403012-f909-414d-bb21-a012dc5722c2,894.0
|
||||
2025-09-01 17:35:29+08:00,2025-09-01 17:38:36+08:00,4473.01,4460.17,300.0,3852.0000000000437,short,67981b9f-af13-4595-9a36-80db526ac0e4,de9b300a-19de-4cc4-b86e-e5d8ade4eb2f,
|
||||
2025-09-01 17:35:29+08:00,2025-09-01 17:38:36+08:00,4473.01,4460.17,70.0,898.8000000000102,short,67981b9f-af13-4595-9a36-80db526ac0e4,f7f5c314-6e7b-498a-99cf-a2c868021b32,
|
||||
2025-09-01 17:35:29+08:00,2025-09-01 17:38:36+08:00,4473.01,4460.17,236.0,3030.2400000000343,short,67981b9f-af13-4595-9a36-80db526ac0e4,f34ae213-3bb5-4bc5-96b5-1efb92648427,
|
||||
2025-09-01 17:35:29+08:00,2025-09-01 17:38:36+08:00,4473.01,4460.17,287.0,3685.0800000000418,short,da23b541-1bde-4417-8875-4a52000d3183,f34ae213-3bb5-4bc5-96b5-1efb92648427,
|
||||
|
BIN
价格展示/做市策略.xls
BIN
价格展示/做市策略.xls
Binary file not shown.
177
回测数据/test2.py
177
回测数据/test2.py
@@ -1,177 +0,0 @@
|
||||
import datetime
|
||||
import requests
|
||||
from loguru import logger
|
||||
|
||||
# 请求头,用于伪装成浏览器访问接口
|
||||
headers = {
|
||||
'accept': 'application/json, text/plain, */*',
|
||||
'accept-language': 'zh,zh-CN;q=0.9,zh-HK;q=0.8,en;q=0.7',
|
||||
'cache-control': 'no-cache',
|
||||
'origin': 'https://www.websea.com',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=1, i',
|
||||
'referer': 'https://www.websea.com/',
|
||||
'sec-ch-ua': '"Chromium";v="140", "Not=A?Brand";v="24", "Google Chrome";v="140"',
|
||||
'sec-ch-ua-mobile': '?0',
|
||||
'sec-ch-ua-platform': '"Windows"',
|
||||
'sec-fetch-dest': 'empty',
|
||||
'sec-fetch-mode': 'cors',
|
||||
'sec-fetch-site': 'same-site',
|
||||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36',
|
||||
}
|
||||
|
||||
|
||||
def fetch_kline(day: int):
|
||||
"""获取某一天的分钟级 K线数据"""
|
||||
# 构造该日的起止时间戳
|
||||
time_ser = datetime.datetime(2025, 6, day)
|
||||
start_of_day = time_ser.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
end_of_day = time_ser.replace(hour=23, minute=59, second=59, microsecond=0)
|
||||
params = {
|
||||
'symbol': 'ETH-USDT', # 交易对
|
||||
'period': '1min', # 分钟级K线
|
||||
'start': int(start_of_day.timestamp()), # 开始时间
|
||||
'end': int(end_of_day.timestamp()), # 结束时间
|
||||
}
|
||||
# 请求 API 获取数据
|
||||
response = requests.get('https://capi.websea.com/webApi/market/getKline', params=params, headers=headers)
|
||||
data = response.json()['result']['data']
|
||||
# 按 id 排序(保证时间顺序)
|
||||
return sorted(data, key=lambda x: x['id'])
|
||||
|
||||
|
||||
# ================= 辅助函数 =================
|
||||
|
||||
def is_bullish(candle):
|
||||
"""判断是否是阳线(收盘价 > 开盘价)"""
|
||||
return float(candle['close']) > float(candle['open'])
|
||||
|
||||
|
||||
def is_bearish(candle):
|
||||
"""判断是否是阴线(收盘价 < 开盘价)"""
|
||||
return float(candle['close']) < float(candle['open'])
|
||||
|
||||
|
||||
def check_signal(prev, curr):
|
||||
"""
|
||||
判断是否出现反包形态:
|
||||
1. 前一根阴线,后一根阳线,并且阳线包住前一根阴线 -> 做多信号
|
||||
2. 前一根阳线,后一根阴线,并且阴线包住前一根阳线 -> 做空信号
|
||||
"""
|
||||
p_open, p_close = float(prev['open']), float(prev['close'])
|
||||
c_open, c_close = float(curr['open']), float(curr['close'])
|
||||
|
||||
# 情况1:前阴后阳,且阳线包住前阴
|
||||
if is_bullish(curr) and is_bearish(prev):
|
||||
if c_open < p_close and c_close > p_open:
|
||||
return "long"
|
||||
|
||||
# 情况2:前阳后阴,且阴线包住前阳
|
||||
if is_bearish(curr) and is_bullish(prev):
|
||||
if c_open > p_close and c_close < p_open:
|
||||
return "short"
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def simulate_trade(direction, entry_price, future_candles, capital=10000, take_profit_ratio=5,
|
||||
stop_loss_ratio=-2):
|
||||
"""
|
||||
模拟交易(逐根K线回测)
|
||||
使用资金金额来控制止盈止损:
|
||||
- 盈利达到 capital * take_profit_ratio 就止盈
|
||||
- 亏损达到 capital * stop_loss_ratio 就止损
|
||||
"""
|
||||
|
||||
for candle in future_candles:
|
||||
high, low, close = float(candle['high']), float(candle['low']), float(candle['close'])
|
||||
|
||||
if direction == "long":
|
||||
if high - entry_price >= take_profit_ratio:
|
||||
return high, take_profit_ratio, candle['id'] # 止盈
|
||||
if low - entry_price <= stop_loss_ratio:
|
||||
return low, stop_loss_ratio, candle['id'] # 止损
|
||||
|
||||
elif direction == "short":
|
||||
if entry_price - low >= take_profit_ratio:
|
||||
return high, take_profit_ratio, candle['id'] # 止盈
|
||||
if entry_price - low <= stop_loss_ratio:
|
||||
return low, stop_loss_ratio, candle['id'] # 止损
|
||||
|
||||
# 如果未来都没触发,最后一根收盘平仓
|
||||
final_price = float(future_candles[-1]['close'])
|
||||
if direction == "long":
|
||||
diff_money = (final_price - entry_price) / entry_price * capital
|
||||
else:
|
||||
diff_money = (entry_price - final_price) / entry_price * capital
|
||||
|
||||
return final_price, diff_money, future_candles[-1]['id']
|
||||
|
||||
|
||||
# ================= 主程序 =================
|
||||
if __name__ == '__main__':
|
||||
if __name__ == '__main__':
|
||||
zh_project = 0 # 累计盈亏
|
||||
all_trades = [] # 保存所有交易明细
|
||||
|
||||
# 遍历 9月1日 ~ 9月27日
|
||||
for i in range(1, 30):
|
||||
sorted_data = fetch_kline(i) # 获取数据
|
||||
|
||||
signals = wins = 0
|
||||
lig_price = low_price = 0 # 分别记录多/空的净收益
|
||||
|
||||
# 遍历每根K线,寻找信号
|
||||
for idx in range(1, len(sorted_data) - 2): # 留出未来K线
|
||||
prev, curr = sorted_data[idx - 1], sorted_data[idx] # 前一笔,当前一笔
|
||||
entry_candle = sorted_data[idx + 1] # 下一根开盘价作为入场价
|
||||
future_candles = sorted_data[idx + 2:] # 未来行情
|
||||
|
||||
entry_open = float(entry_candle['open']) # 开仓价格
|
||||
direction = check_signal(prev, curr) # 判断开仓方向
|
||||
|
||||
if direction:
|
||||
signals += 1 # 总共信号数
|
||||
exit_price, diff, exit_time = simulate_trade(direction, entry_open, future_candles)
|
||||
|
||||
# 统计多单/空单盈亏情况
|
||||
if direction == "long":
|
||||
lig_price += diff
|
||||
if diff > 0: wins += 1
|
||||
else:
|
||||
low_price += diff
|
||||
if diff > 0: wins += 1
|
||||
|
||||
# 保存交易详情
|
||||
all_trades.append((f"{i}号", entry_candle["id"], "做多" if direction == "long" else "做空",
|
||||
entry_open, exit_price, diff, exit_time))
|
||||
|
||||
# 输出每日统计结果
|
||||
if signals > 0:
|
||||
logger.info(
|
||||
f"日期:{i}号,信号数={signals}, 胜率={wins / signals * 100:.2f}%,"
|
||||
f"上涨方向={lig_price:.2f},下跌方向={low_price:.2f},综合={lig_price + low_price:.2f}"
|
||||
)
|
||||
else:
|
||||
logger.info(f"日期:{i}号,没有信号")
|
||||
|
||||
# 累计盈亏
|
||||
zh_project += (lig_price + low_price)
|
||||
|
||||
logger.success(f"综合价格:{zh_project:.2f}")
|
||||
|
||||
# ===== 输出每笔交易详情 =====
|
||||
logger.info("===== 每笔交易详情 =====")
|
||||
n = n1 = 0 # n = 总盈利,n1 = 总手续费
|
||||
for date, time_str, direction, entry, exit, diff, end_time in all_trades:
|
||||
logger.info(
|
||||
f"{date} {time_str} {direction} 入场={entry:.2f} 出场={exit:.2f} 出场时间={end_time} "
|
||||
f"差价={diff:.2f} 盈利={diff / entry * 10000:.2f} "
|
||||
f"开仓手续费=5u 平仓手续费={10000 / entry * exit * 0.0005:.2f}"
|
||||
)
|
||||
n1 += 5 + (10000 / entry * exit * 0.0005)
|
||||
n += (diff / entry) * 10000
|
||||
|
||||
print(f'一共笔数:{len(all_trades)}')
|
||||
print(f"一共盈利:{n:.2f}")
|
||||
print(f'一共手续费:{n1:.2f}')
|
||||
214
回测数据/推测策略,回测.py
214
回测数据/推测策略,回测.py
@@ -1,214 +0,0 @@
|
||||
import datetime
|
||||
import requests
|
||||
from loguru import logger
|
||||
|
||||
# 请求头,用于伪装成浏览器访问接口
|
||||
headers = {
|
||||
'accept': 'application/json, text/plain, */*',
|
||||
'accept-language': 'zh,zh-CN;q=0.9,zh-HK;q=0.8,en;q=0.7',
|
||||
'cache-control': 'no-cache',
|
||||
'origin': 'https://www.websea.com',
|
||||
'pragma': 'no-cache',
|
||||
'priority': 'u=1, i',
|
||||
'referer': 'https://www.websea.com/',
|
||||
'sec-ch-ua': '"Chromium";v="140", "Not=A?Brand";v="24", "Google Chrome";v="140"',
|
||||
'sec-ch-ua-mobile': '?0',
|
||||
'sec-ch-ua-platform': '"Windows"',
|
||||
'sec-fetch-dest': 'empty',
|
||||
'sec-fetch-mode': 'cors',
|
||||
'sec-fetch-site': 'same-site',
|
||||
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36',
|
||||
}
|
||||
|
||||
|
||||
def fetch_kline(day: int, year: int = 2025, month: int = 9, period=1):
|
||||
"""获取某一天的分钟级 K线数据"""
|
||||
# 构造该日的起止时间戳
|
||||
time_ser = datetime.datetime(year, month, day) # 修正为2024年9月
|
||||
start_of_day = time_ser.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
end_of_day = time_ser.replace(hour=23, minute=59, second=59, microsecond=0)
|
||||
params = {
|
||||
'symbol': 'ETH-USDT', # 交易对
|
||||
'period': f'{period}min', # 分钟级K线
|
||||
'start': int(start_of_day.timestamp()), # 开始时间
|
||||
'end': int(end_of_day.timestamp()), # 结束时间
|
||||
}
|
||||
# 请求 API 获取数据
|
||||
response = requests.get('https://capi.websea.com/webApi/market/getKline', params=params, headers=headers)
|
||||
data = response.json()['result']['data']
|
||||
# 按 id 排序(保证时间顺序)
|
||||
return sorted(data, key=lambda x: x['id'])
|
||||
|
||||
|
||||
# ================= 辅助函数 =================
|
||||
|
||||
def is_bullish(candle):
|
||||
"""判断是否是阳线(开盘价 < 收盘价,即涨)"""
|
||||
return float(candle['open']) < float(candle['close'])
|
||||
|
||||
|
||||
def is_bearish(candle):
|
||||
"""判断是否是阴线(开盘价 > 收盘价,即跌)"""
|
||||
return float(candle['open']) > float(candle['close'])
|
||||
|
||||
|
||||
def check_signal(prev, curr):
|
||||
"""
|
||||
判断是否出现包住形态,返回信号类型和方向:
|
||||
1. 前跌后涨包住 -> 做多信号 (bear_bull_engulf)
|
||||
2. 前涨后跌包住 -> 做空信号 (bull_bear_engulf)
|
||||
3. 前涨后涨包住 -> 做多信号 (bull_bull_engulf)
|
||||
4. 前跌后跌包住 -> 做空信号 (bear_bear_engulf)
|
||||
"""
|
||||
p_open, p_close = float(prev['open']), float(prev['close']) # 前一笔
|
||||
c_open, c_close = float(curr['open']), float(curr['close']) # 当前一笔
|
||||
|
||||
# 情况1:前跌后涨,且涨线包住前跌线 -> 做多信号
|
||||
if is_bullish(curr) and is_bearish(prev):
|
||||
if c_open < p_close and c_close > p_open:
|
||||
return "long", "bear_bull_engulf"
|
||||
|
||||
# 情况2:前涨后跌,且跌线包住前涨线 -> 做空信号
|
||||
if is_bearish(curr) and is_bullish(prev):
|
||||
if c_open > p_close and c_close < p_open:
|
||||
return "short", "bull_bear_engulf"
|
||||
|
||||
# # 情况3:前涨后涨,且后涨线包住前涨线 -> 做多信号
|
||||
# if is_bullish(curr) and is_bullish(prev):
|
||||
# if c_open < p_open and c_close > p_close:
|
||||
# return "long", "bull_bull_engulf"
|
||||
|
||||
# # 情况4:前跌后跌,且后跌线包住前跌线 -> 做空信号
|
||||
# if is_bearish(curr) and is_bearish(prev):
|
||||
# if c_open > p_open and c_close < p_close:
|
||||
# return "short", "bear_bear_engulf"
|
||||
|
||||
return None, None
|
||||
|
||||
|
||||
def simulate_trade(direction, entry_price, future_candles, take_profit_diff=30, stop_loss_diff=-10):
|
||||
"""
|
||||
模拟交易(逐根K线回测)
|
||||
使用价差来控制止盈止损:
|
||||
- 盈利达到 take_profit_diff 就止盈
|
||||
- 亏损达到 stop_loss_diff 就止损
|
||||
|
||||
direction:信号类型
|
||||
entry_price:开盘价格
|
||||
future_candles:未来的行情数据
|
||||
|
||||
"""
|
||||
|
||||
for candle in future_candles:
|
||||
high, low, close = float(candle['high']), float(candle['low']), float(candle['close'])
|
||||
|
||||
if direction == "long":
|
||||
# 做多:检查最高价是否达到止盈,最低价是否触及止损
|
||||
if high >= entry_price + take_profit_diff:
|
||||
return entry_price + take_profit_diff, take_profit_diff, candle['id'] # 止盈
|
||||
if low <= entry_price + stop_loss_diff:
|
||||
return entry_price + stop_loss_diff, stop_loss_diff, candle['id'] # 止损
|
||||
|
||||
elif direction == "short":
|
||||
# 做空:检查最低价是否达到止盈,最高价是否触及止损
|
||||
if low <= entry_price - take_profit_diff:
|
||||
return entry_price - take_profit_diff, take_profit_diff, candle['id'] # 止盈
|
||||
if high >= entry_price - stop_loss_diff:
|
||||
return entry_price - stop_loss_diff, stop_loss_diff, candle['id'] # 止损
|
||||
|
||||
# 如果未来都没触发,最后一根收盘平仓
|
||||
final_price = float(future_candles[-1]['close'])
|
||||
if direction == "long":
|
||||
diff_money = final_price - entry_price
|
||||
else:
|
||||
diff_money = entry_price - final_price
|
||||
|
||||
return final_price, diff_money, future_candles[-1]['id']
|
||||
|
||||
|
||||
# ================= 主程序 =================
|
||||
if __name__ == '__main__':
|
||||
zh_project = 0 # 累计盈亏
|
||||
all_trades = [] # 保存所有交易明细
|
||||
|
||||
# 四种信号类型的统计
|
||||
signal_stats = {
|
||||
"bear_bull_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "涨包跌"},
|
||||
"bull_bear_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "跌包涨"},
|
||||
# "bull_bull_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "涨包涨"},
|
||||
# "bear_bear_engulf": {"count": 0, "wins": 0, "total_profit": 0, "name": "跌包跌"}
|
||||
}
|
||||
|
||||
datas = []
|
||||
# 遍历 9月1日 ~ 9月27日
|
||||
for i in range(1, 31):
|
||||
logger.info(i)
|
||||
sorted_data = fetch_kline(year=2025, month=9, day=i, period=15) # 获取数据
|
||||
|
||||
datas.extend(sorted_data)
|
||||
|
||||
datas = sorted(datas, key=lambda x: x["id"])
|
||||
print(datas)
|
||||
daily_signals = 0 # 信号总数
|
||||
daily_wins = 0
|
||||
daily_profit = 0 # 价差总和
|
||||
|
||||
# 遍历每根K线,寻找信号
|
||||
for idx in range(1, len(datas) - 2): # 留出未来K线
|
||||
prev, curr = datas[idx - 1], datas[idx] # 前一笔,当前一笔
|
||||
entry_candle = datas[idx + 1] # 下一根开盘价作为入场价
|
||||
future_candles = datas[idx + 2:] # 未来行情
|
||||
|
||||
entry_open = float(entry_candle['open']) # 开仓价格
|
||||
direction, signal_type = check_signal(prev, curr) # 判断开仓方向和信号类型
|
||||
|
||||
if direction and signal_type:
|
||||
daily_signals += 1
|
||||
|
||||
exit_price, diff, exit_time = simulate_trade(direction, entry_open, future_candles, take_profit_diff=30,
|
||||
stop_loss_diff=-2)
|
||||
|
||||
# 统计该信号类型的表现
|
||||
signal_stats[signal_type]["count"] += 1
|
||||
signal_stats[signal_type]["total_profit"] += diff
|
||||
if diff > 0:
|
||||
signal_stats[signal_type]["wins"] += 1
|
||||
daily_wins += 1
|
||||
|
||||
daily_profit += diff
|
||||
|
||||
# 将时间戳转换为本地时间
|
||||
local_time = datetime.datetime.fromtimestamp(entry_candle['id'])
|
||||
formatted_time = local_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
# 保存交易详情
|
||||
all_trades.append(
|
||||
(
|
||||
f"{formatted_time}号",
|
||||
"做多" if direction == "long" else "做空",
|
||||
signal_stats[signal_type]["name"],
|
||||
entry_open,
|
||||
exit_price,
|
||||
diff,
|
||||
exit_time
|
||||
)
|
||||
)
|
||||
|
||||
# ===== 输出每笔交易详情 =====
|
||||
logger.info("===== 每笔交易详情 =====")
|
||||
n = n1 = 0 # n = 总盈利,n1 = 总手续费
|
||||
for date, direction, signal_name, entry, exit, diff, end_time in all_trades:
|
||||
profit_amount = diff / entry * 10000 # 计算盈利金额
|
||||
close_fee = 10000 / entry * exit * 0.0005 # 平仓手续费
|
||||
|
||||
logger.info(
|
||||
f"{date} {direction}({signal_name}) 入场={entry:.2f} 出场={exit:.2f} 出场时间={end_time} "
|
||||
f"差价={diff:.2f} 盈利={profit_amount:.2f} "
|
||||
f"开仓手续费=5u 平仓手续费={close_fee:.2f}"
|
||||
)
|
||||
n1 += 5 + close_fee
|
||||
n += profit_amount
|
||||
|
||||
print(f'一共笔数:{len(all_trades)}')
|
||||
print(f"一共盈利:{n:.2f}")
|
||||
print(f'一共手续费:{n1:.2f}')
|
||||
Reference in New Issue
Block a user