From 2a7e293d9aaa6b9f846ddb7a63d87906ebfd66b4 Mon Sep 17 00:00:00 2001 From: 27942 <2794236280@qq.com> Date: Thu, 9 Oct 2025 17:47:33 +0800 Subject: [PATCH] gfrdegdergr --- okx/okx_eth_15m_sep2025.json | 1 + okx/test.py | 211 +++++++++++++++++++++++++++++++++++ 2 files changed, 212 insertions(+) create mode 100644 okx/okx_eth_15m_sep2025.json create mode 100644 okx/test.py diff --git a/okx/okx_eth_15m_sep2025.json b/okx/okx_eth_15m_sep2025.json new file mode 100644 index 0000000..0637a08 --- /dev/null +++ b/okx/okx_eth_15m_sep2025.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/okx/test.py b/okx/test.py new file mode 100644 index 0000000..9b89f87 --- /dev/null +++ b/okx/test.py @@ -0,0 +1,211 @@ +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)