Files
lm_code/okx/test.py

212 lines
7.2 KiB
Python
Raw Normal View History

2025-10-09 17:47:33 +08:00
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)