# -*- coding: utf-8 -*-
import os, json, time, hmac, hashlib, base64, requests
from datetime import datetime, timedelta
from websocket import WebSocketApp
from collections import deque
import pandas as pd
import numpy as np
import talib
API_KEY = os.getenv("OKX_APIKEY", "4f2d675a-2691-4545-a76f-f3e7e59a5de2")
SECRET = os.getenv("OKX_SECRET", "7875E6988036A4E990AB78602F317C01")
PASSPHRASE = os.getenv("OKX_PASSPHRASE", "225522Lan@")
WS_URL = "wss://ws.okx.com:8443/ws/v5/business"
REST_URL = "https://www.okx.com"
INST_ID = "SOL-USDT"
CHANNELS = ["candle15m", "candle4H", "candle12H"]
klines_15m = deque(maxlen=2000)
klines_4h = deque(maxlen=2000)
klines_12h = deque(maxlen=2000)
class TradingStrategy:
def __init__(self):
self.phase = 1 # 1: 等待首次入场, 2: 持仓等待有效死叉
self.entry_price = None # 入场价格
self.position_size = 0 # 持仓数量
self.invested_amount = 0 # 投资金额
self.golden_cross_triggered = False # 金叉是否已触发
self.observation_window = 0 # 观察窗口计数
self.golden_cross_price = None # 金叉发生时的价格
self.golden_cross_time = None # 金叉发生的时间
self.death_cross_count = 0 # 死叉计数
def check_trend_condition(self, klines_4h):
"""检查4H趋势条件"""
if len(klines_4h) < 200:
return False
# 计算EMA指标
closes = [k[4] for k in list(klines_4h)]
ema40 = pd.Series(closes).ewm(span=40, adjust=False).mean().iloc[-1]
ema200 = pd.Series(closes).ewm(span=200, adjust=False).mean().iloc[-1]
# 获取当前价格
current_price = klines_4h[-1][4] if klines_4h else None
# 检查趋势条件
if current_price > ema200 * 0.98 and ema40 >= ema200:
return True
return False
def check_close_condition(self, klines_4h):
"""检查关闭条件"""
if len(klines_4h) < 200:
return False
# 计算EMA200
closes = [k[4] for k in list(klines_4h)]
ema200 = pd.Series(closes).ewm(span=200, adjust=False).mean().iloc[-1]
# 获取当前价格
current_price = klines_4h[-1][4] if klines_4h else None
# 检查关闭条件
if current_price < ema200 * 0.97:
return True
return False
def check_golden_cross(self, klines_15m):
"""检查15分钟金叉"""
if len(klines_15m) < 200:
return False
# 计算EMA指标
closes = [k[4] for k in list(klines_15m)]
ema40 = pd.Series(closes).ewm(span=40, adjust=False).mean().iloc[-1]
ema200 = pd.Series(closes).ewm(span=200, adjust=False).mean().iloc[-1]
# 检查金叉条件
if ema40 >= ema200 * 0.998:
return True
return False
def check_buy_condition(self, klines_15m, fib_levels):
"""检查买入条件"""
if not self.golden_cross_triggered or self.observation_window <= 0:
return False
# 计算EMA指标
closes = [k[4] for k in list(klines_15m)]
ema40 = pd.Series(closes).ewm(span=40, adjust=False).mean().iloc[-1]
# 计算RSI
series = pd.Series(closes)
delta = series.diff()
gain = (delta.where(delta > 0, 0)).ewm(alpha=1/14).mean()
loss = (-delta.where(delta < 0, 0)).ewm(alpha=1/14).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
rsi_value = rsi.iloc[-1]
# 获取当前价格
current_price = klines_15m[-1][4] if klines_15m else None
# 获取金叉发生前两根K线的收盘价
if len(klines_15m) >= 3:
prev_close_1 = klines_15m[-2][4] # 上一根K线收盘价
prev_close_2 = klines_15m[-3][4] # 上上一根K线收盘价
else:
return False
# 检查买入条件
condition1 = current_price >= ema40
condition2 = rsi_value > 60.6 or current_price > max(prev_close_1, prev_close_2)
return condition1 and condition2
def check_death_cross(self, klines_15m):
"""检查死叉"""
if len(klines_15m) < 200:
return False
# 计算EMA指标
closes = [k[4] for k in list(klines_15m)]
ema40 = pd.Series(closes).ewm(span=40, adjust=False).mean().iloc[-1]
ema200 = pd.Series(closes).ewm(span=200, adjust=False).mean().iloc[-1]
# 检查死叉条件
return ema40 < ema200 * 0.999
def check_effective_death_cross(self, klines_15m):
"""检查有效死叉"""
if len(klines_15m) < 200:
return False
# 计算EMA200
closes = [k[4] for k in list(klines_15m)]
ema200 = pd.Series(closes).ewm(span=200, adjust=False).mean().iloc[-1]
# 获取最近两根K线的收盘价
if len(klines_15m) >= 2:
close_1 = klines_15m[-1][4] # 当前K线收盘价
close_2 = klines_15m[-2][4] # 上一根K线收盘价
else:
return False
# 检查有效死叉条件
return close_1 < ema200 * 0.999 and close_2 < ema200 * 0.999
def check_profit_taking(self, current_price, fib_levels):
"""检查止盈条件"""
if self.entry_price is None:
return False
# 计算盈利百分比
profit_percent = ((current_price - self.entry_price) / self.entry_price) * 100
# 条件1: 盈利8%
if profit_percent >= 8:
return True
# 条件2: 在斐波那契回撤位附近且盈利8%
for level, price in fib_levels.items():
# 检查是否在回撤位的-2%区间内
if abs(current_price - price) / price <= 0.02 and profit_percent >= 8:
return True
return False
def execute_buy(self, price):
"""执行买入操作"""
self.entry_price = price
self.position_size = 50 / price # 买入50U的SOL
self.invested_amount = 50
self.phase = 2 # 切换到阶段二
print(f"🟢 执行买入: 价格={price}, 数量={self.position_size}, 投资=50U")
def execute_sell(self, price, percent=50):
"""执行卖出操作"""
if self.position_size <= 0:
return
# 计算卖出数量
sell_amount = self.position_size * (percent / 100)
sell_value = sell_amount * price
# 更新持仓
self.position_size -= sell_amount
self.invested_amount -= sell_value
print(f"🟠 执行卖出: 价格={price}, 数量={sell_amount}, 金额={sell_value}U, 剩余持仓={self.position_size}")
# 如果全部卖出,重置状态
if self.position_size <= 0:
self.phase = 1
self.entry_price = None
self.position_size = 0
self.invested_amount = 0
print("🟡 仓位已清空,重置到阶段一")
class OKXWSClient:
def __init__(self):
self.last_15m_ts = None
self.last_4h_ts = None
self.last_12h_ts = None
self.fib_high = None
self.fib_low = None
self.pending_15m_kline = None
self.pending_4h_kline = None
self.pending_12h_kline = None
self.strategy = TradingStrategy() # 添加策略实例
def sign(self, ts, method, path):
msg = ts + method + path
return base64.b64encode(hmac.new(SECRET.encode(), msg.encode(), hashlib.sha256).digest()).decode()
def headers(self, method, path):
ts = datetime.utcnow().isoformat()[:-3] + 'Z'
return {
'Content-Type': 'application/json',
'OK-ACCESS-KEY': API_KEY,
'OK-ACCESS-SIGN': self.sign(ts, method, path),
'OK-ACCESS-TIMESTAMP': ts,
'OK-ACCESS-PASSPHRASE': PASSPHRASE
}
def get(self, path):
try:
return requests.get(REST_URL + path, headers=self.headers("GET", path), timeout=10).json()
except Exception:
return {}
def rest_init(self):
# 账户 / 持仓
print("REST 账户权益:", self.get("/api/v5/account/balance").get("data", [{}])[0].get("totalEq", "N/A"), "USDT")
print("REST 持仓:", self.get("/api/v5/account/positions").get("data", []))
# 历史 K 线(已闭合)
for bar, dq, name, lim in (
("12H", klines_12h, "12H", 200), # 增加到200根
("4H", klines_4h, "4H", 800),
("15m", klines_15m, "15m", 800)):
raw = self.get(f"/api/v5/market/candles?instId={INST_ID}&bar={bar}&limit={lim}").get("data", [])
# 确保数据按正确的时间顺序排列(从旧到新)
for k in reversed(raw):
ts = int(k[0])
o = float(k[1])
h = float(k[2])
l = float(k[3])
c = float(k[4])
vol = float(k[5])
dq.append([ts, o, h, l, c, vol])
print(f"已加载 {len(dq)} 根已闭合 {name} K 线")
# 输出数据范围信息
if dq:
first_ts = dq[0][0]
last_ts = dq[-1][0]
first_dt = datetime.fromtimestamp(first_ts/1000).strftime("%Y-%m-%d %H:%M:%S")
last_dt = datetime.fromtimestamp(last_ts/1000).strftime("%Y-%m-%d %H:%M:%S")
print(f"{name}数据范围: {first_dt} 到 {last_dt}")
# 获取12H数据来计算斐波那契回撤位
highs_12h = [k[2] for k in list(klines_12h)]
lows_12h = [k[3] for k in list(klines_12h)]
# 设置斐波那契回撤的高点和低点
self.fib_high = max(highs_12h)
self.fib_low = min(lows_12h)
print(f"最近 {len(klines_12h)} 根12H线最高: {self.fib_high}, 最低: {self.fib_low}")
# 计算斐波那契回撤位
fib_levels = self.calculate_fib_levels()
# 检查历史数据是否满足趋势条件
if self.strategy.check_trend_condition(klines_4h):
print("🟢 历史数据满足趋势条件,策略已开启")
else:
print("🔴 历史数据不满足趋势条件,策略未开启")
# 输出历史数据的最新4H指标
print("\n=== 历史数据最新指标 ===")
if klines_4h:
self.print_historical_4h_indicators(fib_levels)
print("=== 历史数据指标结束 ===\n")
def calculate_fib_levels(self):
"""计算斐波那契回撤位(基于12H数据)"""
if self.fib_high is None or self.fib_low is None:
return {}
diff = self.fib_high - self.fib_low
# 计算斐波那契回撤位
fib_levels = {
'0.0': self.fib_low,
'0.236': self.fib_low + diff * 0.236,
'0.382': self.fib_low + diff * 0.382,
'0.5': self.fib_low + diff * 0.5,
'0.618': self.fib_low + diff * 0.618,
'0.786': self.fib_low + diff * 0.786,
'1.0': self.fib_high,
'1.272': self.fib_high + diff * 0.272, # 扩展位
'1.618': self.fib_high + diff * 0.618, # 扩展位
}
print("基于12H数据的斐波那契回撤/扩展位:")
for level, price in fib_levels.items():
print(f" {level}: {price:.4f}")
return fib_levels
def calculate_ema(self, data, period):
"""计算EMA指标"""
if len(data) < period:
return None
# 获取收盘价
closes = [d[4] for d in list(data)]
# 使用pandas的EMA计算
series = pd.Series(closes)
ema = series.ewm(span=period, adjust=False).mean().iloc[-1]
return round(ema, 4)
def print_historical_4h_indicators(self, fib_levels):
"""输出历史4H指标(简化版)"""
# 计算EMA指标
ema40 = self.calculate_ema(klines_4h, 40)
ema200 = self.calculate_ema(klines_4h, 200)
# 获取当前K线
k = klines_4h[-1] if klines_4h else None
if not k:
return
ts, o, h, l, c, vol = k
dt = datetime.fromtimestamp(ts/1000).strftime("%Y-%m-%d %H:%M:%S")
# 输出指标
out = f"[4H 闭合] {dt}\n"
out += f" O={o:.4f} H={h:.4f} L={l:.4f} C={c:.4f} V={vol:.4f}\n"
out += f" 4H-40EMA={ema40}\n"
out += f" 4H-200EMA={ema200}\n"
# 添加策略条件判断
if ema40 is not None and ema200 is not None:
if ema40 > ema200 and c > ema200:
out += " 🟢 策略条件满足: 4H40EMA > 4H200EMA 且 收盘价 > 4H200EMA\n"
else:
out += " 🔴 策略条件未满足\n"
print(out)
def print_indicators(self, period):
"""实时数据指标输出"""
data = klines_15m if period == "15m" else (klines_4h if period == "4H" else klines_12h)
# 计算EMA指标
ema40 = self.calculate_ema(data, 40)
ema200 = self.calculate_ema(data, 200)
# 计算RSI
rsi15 = self.calculate_rsi(klines_15m, 14) if period == "15m" else None
# 获取当前K线
k = data[-1] if data else None
if not k:
return
ts, o, h, l, c, vol = k
dt = datetime.fromtimestamp(ts/1000).strftime("%Y-%m-%d %H:%M:%S")
# 输出指标
out = f"[{period} 闭合] {dt}\n"
out += f" O={o:.4f} H={h:.4f} L={l:.4f} C={c:.4f} V={vol:.4f}\n"
if period == "4H":
out += f" 4H-40EMA={ema40}\n"
out += f" 4H-200EMA={ema200}\n"
# 添加策略条件判断
if ema40 is not None and ema200 is not None:
if ema40 > ema200 and c > ema200:
out += " 🟢 策略条件满足: 4H40EMA > 4H200EMA 且 收盘价 > 4H200EMA\n"
else:
out += " 🔴 策略条件未满足\n"
# 输出斐波那契回撤位信息
if self.fib_high is not None and self.fib_low is not None:
fib_levels = self.calculate_fib_levels()
out += " 基于12H的斐波那契回撤/扩展位:\n"
for level, price in fib_levels.items():
distance_pct = ((c - price) / price) * 100
out += f" {level}: {price:.4f} (距离: {distance_pct:+.2f}%)\n"
elif period == "12H":
out += f" 12H-40EMA={ema40}\n"
out += f" 12H-200EMA={ema200}\n"
# 输出斐波那契回撤位信息
if self.fib_high is not None and self.fib_low is not None:
fib_levels = self.calculate_fib_levels()
out += " 基于12H的斐波那契回撤/扩展位:\n"
for level, price in fib_levels.items():
distance_pct = ((c - price) / price) * 100
out += f" {level}: {price:.4f} (距离: {distance_pct:+.2f}%)\n"
else:
out += f" 15m-40EMA={ema40}\n"
out += f" 15m-200EMA={ema200}\n"
if rsi15:
out += f" 15m-RSI={rsi15}\n"
# 输出数据点数量信息
out += f" 数据点数量: 15m={len(klines_15m)}, 4H={len(klines_4h)}, 12H={len(klines_12h)}\n"
print(out)
# 策略逻辑处理
self.process_strategy(c, period)
def process_strategy(self, current_price, period):
"""处理策略逻辑"""
# 只处理15分钟K线
if period != "15m":
return
# 计算斐波那契回撤位
fib_levels = self.calculate_fib_levels()
# 检查趋势条件
trend_condition = self.strategy.check_trend_condition(klines_4h)
# 检查关闭条件
close_condition = self.strategy.check_close_condition(klines_4h)
if close_condition:
print("🔴 关闭条件满足,策略停止")
return
if not trend_condition:
print("🔴 趋势条件不满足,策略未开启")
return
print("🟢 趋势条件满足,策略运行中")
# 阶段一:等待首次入场
if self.strategy.phase == 1:
# 检查金叉
if self.strategy.check_golden_cross(klines_15m) and not self.strategy.golden_cross_triggered:
self.strategy.golden_cross_triggered = True
self.strategy.observation_window = 3
self.strategy.golden_cross_price = current_price
self.strategy.golden_cross_time = datetime.now()
print(f"🟡 金叉触发,开始3根K线观察窗口")
# 更新观察窗口
if self.strategy.golden_cross_triggered:
self.strategy.observation_window -= 1
print(f"🟡 观察窗口剩余: {self.strategy.observation_window}根K线")
# 检查买入条件
if self.strategy.check_buy_condition(klines_15m, fib_levels):
self.strategy.execute_buy(current_price)
# 观察窗口结束
if self.strategy.observation_window <= 0:
self.strategy.golden_cross_triggered = False
print("🟡 观察窗口结束,未触发买入条件")
# 阶段二:持仓,等待有效死叉
elif self.strategy.phase == 2:
# 检查死叉
if self.strategy.check_death_cross(klines_15m):
self.strategy.death_cross_count += 1
print(f"🟡 死叉检测到,计数: {self.strategy.death_cross_count}")
# 检查有效死叉
if self.strategy.check_effective_death_cross(klines_15m):
print("🟡 有效死叉确认,重置到阶段一")
self.strategy.phase = 1
self.strategy.death_cross_count = 0
# 检查止盈条件
if self.strategy.check_profit_taking(current_price, fib_levels):
self.strategy.execute_sell(current_price, 50)
print("🟠 止盈条件满足,卖出50%仓位")
def calculate_rsi(self, data, period=14):
"""计算RSI指标"""
if len(data) < period + 1:
return None
# 获取收盘价
closes = [d[4] for d in list(data)]
# 使用pandas计算RSI
series = pd.Series(closes)
delta = series.diff()
gain = (delta.where(delta > 0, 0)).ewm(alpha=1/period).mean()
loss = (-delta.where(delta < 0, 0)).ewm(alpha=1/period).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
return round(rsi.iloc[-1], 2)
def on_msg(self, ws, msg):
try:
data = json.loads(msg)
if "event" in data and data["event"] == "subscribe":
print("✅ 订阅成功", data["arg"])
return
if "data" not in data:
return
arg = data["arg"]
ch = arg["channel"]
k = data["data"][0]
ts = int(k[0])
kline = [ts, float(k[1]), float(k[2]), float(k[3]), float(k[4]), float(k[5])]
if ch == "candle15m":
if self.last_15m_ts is None or ts != self.last_15m_ts:
# 时间戳变化,表示新K线开始,上一根K线已完成
if self.pending_15m_kline is not None:
# 将已完成的K线添加到队列
klines_15m.append(self.pending_15m_kline)
self.print_indicators("15m")
# 保存新K线
self.pending_15m_kline = kline
self.last_15m_ts = ts
else:
# 同一根K线的更新,更新暂存的K线
self.pending_15m_kline = kline
elif ch == "candle4H":
if self.last_4h_ts is None or ts != self.last_4h_ts:
# 时间戳变化,表示新K线开始,上一根K线已完成
if self.pending_4h_kline is not None:
# 将已完成的K线添加到队列
klines_4h.append(self.pending_4h_kline)
self.print_indicators("4H")
# 保存新K线
self.pending_4h_kline = kline
self.last_4h_ts = ts
else:
# 同一根K线的更新,更新暂存的K线
self.pending_4h_kline = kline
elif ch == "candle12H":
if self.last_12h_ts is None or ts != self.last_12h_ts:
# 时间戳变化,表示新K线开始,上一根K线已完成
if self.pending_12h_kline is not None:
# 将已完成的K线添加到队列
klines_12h.append(self.pending_12h_kline)
self.print_indicators("12H")
# 更新斐波那契回撤位
highs_12h = [k[2] for k in list(klines_12h)]
lows_12h = [k[3] for k in list(klines_12h)]
self.fib_high = max(highs_12h)
self.fib_low = min(lows_12h)
print(f"更新斐波那契回撤位: 最高={self.fib_high}, 最低={self.fib_low}")
# 保存新K线
self.pending_12h_kline = kline
self.last_12h_ts = ts
else:
# 同一根K线的更新,更新暂存的K线
self.pending_12h_kline = kline
except Exception as e:
print("处理消息时出错:", e)
def on_open(self, ws):
print("WebSocket连接已建立")
for ch in CHANNELS:
ws.send(json.dumps({"op": "subscribe", "args": [{"channel": ch, "instId": INST_ID}]}))
print(f"已订阅 {ch} {INST_ID}")
def on_error(self, ws, error):
print("WebSocket错误:", error)
def on_close(self, ws, *_):
print("WebSocket连接关闭,5秒后重连...")
time.sleep(5)
self.connect()
def connect(self):
self.ws = WebSocketApp(WS_URL,
on_open=self.on_open,
on_message=self.on_msg,
on_error=self.on_error,
on_close=self.on_close)
self.ws.run_forever()
def run(self):
self.rest_init()
self.connect()
if __name__ == "__main__":
OKXWSClient().run()根据以上的脚本,帮我写一个关于这个脚本的回测策略,让我精确的知道这个脚本是否好用,以及能够盈利等信息
最新发布