support get market data
This commit is contained in:
parent
ebf90534ae
commit
c670be6f7d
32
config.py
32
config.py
|
|
@ -4,16 +4,24 @@
|
||||||
# API_KEY = "7286d434-225b-401f-b3af-fd595e15d23f"
|
# API_KEY = "7286d434-225b-401f-b3af-fd595e15d23f"
|
||||||
# SECRET_KEY = "80B95C5757F9208F70282A85C9DDBC86"
|
# SECRET_KEY = "80B95C5757F9208F70282A85C9DDBC86"
|
||||||
# PASSPHRASE = "Bengbu_2001"
|
# PASSPHRASE = "Bengbu_2001"
|
||||||
|
# SANDBOX = False
|
||||||
|
|
||||||
|
# 实盘读取API密钥配置
|
||||||
|
API_KEY = "a73f9096-8e76-49ff-947c-a4f4edf657ec"
|
||||||
|
SECRET_KEY = "F7AD69272FBF7C44E69CC110D2EDDB7A"
|
||||||
|
PASSPHRASE = "Bengbu!2001"
|
||||||
|
SANDBOX = False
|
||||||
|
|
||||||
# 模拟盘API密钥配置
|
# 模拟盘API密钥配置
|
||||||
API_KEY = "f309e789-3497-4ed3-896f-d18bdc4d9817"
|
# API_KEY = "f309e789-3497-4ed3-896f-d18bdc4d9817"
|
||||||
SECRET_KEY = "9152809391B110E2E647FDE12A37E96D"
|
# SECRET_KEY = "9152809391B110E2E647FDE12A37E96D"
|
||||||
PASSPHRASE = "Bengbu@2001"
|
# PASSPHRASE = "Bengbu@2001"
|
||||||
|
# SANDBOX = True
|
||||||
|
|
||||||
# 交易配置
|
# 交易配置
|
||||||
TRADING_CONFIG = {
|
TRADING_CONFIG = {
|
||||||
"symbol": "BTC-USDT", # 交易对
|
"symbol": "BTC-USDT", # 交易对
|
||||||
"position_size": 0.001, # 每次交易数量(BTC)
|
"position_size": 0.001, # 每次交易数量(BTC)
|
||||||
"sandbox": True, # 是否使用沙盒环境(建议先用沙盒测试)
|
|
||||||
|
|
||||||
# 策略参数
|
# 策略参数
|
||||||
"sma_short_period": 5, # 短期移动平均线周期
|
"sma_short_period": 5, # 短期移动平均线周期
|
||||||
|
|
@ -39,3 +47,19 @@ TIME_CONFIG = {
|
||||||
"kline_interval": "5m", # K线数据间隔
|
"kline_interval": "5m", # K线数据间隔
|
||||||
"kline_limit": 100, # K线数据条数
|
"kline_limit": 100, # K线数据条数
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MONITOR_CONFIG = {
|
||||||
|
"volume_monitor":{
|
||||||
|
"symbols": ["XCH-USDT"],
|
||||||
|
"intervals": ["5m", "15m", "1H", "4H", "1D"],
|
||||||
|
"initial_date": "2025-07-01 00:00:00"
|
||||||
|
},
|
||||||
|
"price_monitor":{
|
||||||
|
"symbols": ["XCH-USDT"],
|
||||||
|
"intervals": [
|
||||||
|
{"interval": "5m", "threshold": 0.025},
|
||||||
|
{"interval": "15m", "threshold": 0.5},
|
||||||
|
{"interval": "1H", "threshold": 0.1}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
21
core/base.py
21
core/base.py
|
|
@ -101,14 +101,31 @@ class QuantTrader:
|
||||||
logging.error(f"获取价格异常: {e}")
|
logging.error(f"获取价格异常: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_kline_data(self, bar: str = '1m', limit: int = 100) -> Optional[pd.DataFrame]:
|
def get_kline_data(self, symbol: str = None, after: str = None, before: str = None, bar: str = '1m', limit: int = 100) -> Optional[pd.DataFrame]:
|
||||||
"""获取K线数据"""
|
"""获取K线数据"""
|
||||||
|
if symbol is None:
|
||||||
|
symbol = self.symbol
|
||||||
try:
|
try:
|
||||||
|
if after is None and before is None:
|
||||||
result = self.market_api.get_candlesticks(
|
result = self.market_api.get_candlesticks(
|
||||||
instId=self.symbol,
|
instId=symbol,
|
||||||
bar=bar,
|
bar=bar,
|
||||||
limit=str(limit)
|
limit=str(limit)
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
if after is None:
|
||||||
|
after = ''
|
||||||
|
if before is None:
|
||||||
|
before = ''
|
||||||
|
if limit == 0:
|
||||||
|
limit = ''
|
||||||
|
result = self.market_api.get_candlesticks(
|
||||||
|
instId=symbol,
|
||||||
|
after=after,
|
||||||
|
before=before,
|
||||||
|
bar=bar,
|
||||||
|
limit=str(limit),
|
||||||
|
)
|
||||||
if result.get('code') == '0':
|
if result.get('code') == '0':
|
||||||
data = result.get('data', [])
|
data = result.get('data', [])
|
||||||
if not data:
|
if not data:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
import time
|
||||||
|
from datetime import datetime, timedelta
|
||||||
|
import logging
|
||||||
|
from typing import Optional
|
||||||
|
import pandas as pd
|
||||||
|
import okx.MarketData as Market
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s: %(message)s')
|
||||||
|
|
||||||
|
class DataMonitor:
|
||||||
|
def __init__(self,
|
||||||
|
api_key: str,
|
||||||
|
secret_key: str,
|
||||||
|
passphrase: str,
|
||||||
|
sandbox: bool = True):
|
||||||
|
flag = "1" if sandbox else "0" # 0:实盘环境 1:沙盒环境
|
||||||
|
self.market_api = Market.MarketAPI(
|
||||||
|
api_key=api_key, api_secret_key=secret_key, passphrase=passphrase,
|
||||||
|
flag=flag
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_historical_kline_data(self, symbol: str = None, start: str = None, bar: str = '1m', limit: int = 100, end_time: int = None) -> Optional[pd.DataFrame]:
|
||||||
|
"""
|
||||||
|
获取历史K线数据,支持start为北京时间字符串(%Y-%m-%d %H:%M:%S)或UTC毫秒级时间戳
|
||||||
|
:param symbol: 交易对
|
||||||
|
:param start: 起始时间(北京时间字符串或UTC毫秒级时间戳)
|
||||||
|
:param bar: K线周期
|
||||||
|
:param limit: 每次请求数量
|
||||||
|
:param end_time: 结束时间(毫秒级timestamp),默认当前时间
|
||||||
|
:return: pd.DataFrame
|
||||||
|
"""
|
||||||
|
from core.utils import datetime_to_timestamp
|
||||||
|
if symbol is None:
|
||||||
|
symbol = "XCH-USDT"
|
||||||
|
if end_time is None:
|
||||||
|
end_time = int(time.time() * 1000) # 当前时间(毫秒)
|
||||||
|
# 处理start参数
|
||||||
|
if start is None:
|
||||||
|
# 默认两个月前
|
||||||
|
two_months_ago = datetime.now() - timedelta(days=60)
|
||||||
|
start_time = int(two_months_ago.timestamp() * 1000)
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
# 判断是否为纯数字(UTC毫秒级timestamp)
|
||||||
|
if start.isdigit():
|
||||||
|
start_time = int(start)
|
||||||
|
else:
|
||||||
|
# 按北京时间字符串处理,转换为毫秒级timestamp
|
||||||
|
start_time = datetime_to_timestamp(start)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"start参数解析失败: {e}")
|
||||||
|
return None
|
||||||
|
columns = ["timestamp", "open", "high", "low", "close", "volume", "volCcy", "volCCyQuote", "confirm"]
|
||||||
|
all_data = []
|
||||||
|
while start_time < end_time:
|
||||||
|
try:
|
||||||
|
# after,真实逻辑是获得指定时间之前的数据 !!!
|
||||||
|
response = self.market_api.get_history_candlesticks(
|
||||||
|
instId=symbol,
|
||||||
|
after=end_time, # 获取指定时间之前的数据,
|
||||||
|
bar=bar,
|
||||||
|
limit=str(limit)
|
||||||
|
)
|
||||||
|
if response["code"] != "0" or not response["data"]:
|
||||||
|
logging.warning(f"请求失败或无数据: {response.get('msg', 'No message')}")
|
||||||
|
break
|
||||||
|
candles = response["data"]
|
||||||
|
all_data.extend(candles)
|
||||||
|
# 更新 end_time 为本次请求中最早的时间戳
|
||||||
|
end_time = int(candles[-1][0])
|
||||||
|
logging.info(f"已获取 {len(candles)} 条数据,最早时间: {pd.to_datetime(end_time, unit='ms', utc=True).tz_convert('Asia/Shanghai')}")
|
||||||
|
end_time -= 1 # 减 1 毫秒以避免重复
|
||||||
|
time.sleep(0.2)
|
||||||
|
except Exception as e:
|
||||||
|
logging.error(f"请求出错: {e}")
|
||||||
|
break
|
||||||
|
if all_data:
|
||||||
|
df = pd.DataFrame(all_data, columns=columns)
|
||||||
|
df = df[df['confirm'] == '1']
|
||||||
|
for col in ['open', 'high', 'low', 'close', 'volume']:
|
||||||
|
df[col] = pd.to_numeric(df[col], errors='coerce')
|
||||||
|
dt_series = pd.to_datetime(df['timestamp'].astype(int), unit='ms', utc=True, errors='coerce').dt.tz_convert('Asia/Shanghai')
|
||||||
|
df['date_time'] = dt_series.dt.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
df = df[['timestamp', 'date_time', 'open', 'high', 'low', 'close', 'volume']]
|
||||||
|
df.sort_values('timestamp', inplace=True)
|
||||||
|
df.reset_index(drop=True, inplace=True)
|
||||||
|
logging.info(f"总计获取 {len(df)} 条 K 线数据(仅confirm=1)")
|
||||||
|
return df
|
||||||
|
else:
|
||||||
|
logging.warning("未获取到数据")
|
||||||
|
return None
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
from datetime import datetime, timezone, timedelta
|
||||||
|
|
||||||
|
def datetime_to_timestamp(date_str: str) -> int:
|
||||||
|
"""
|
||||||
|
将日期字符串(如 '2023-01-01 12:00:00')直接转换为毫秒级时间戳
|
||||||
|
:param date_str: 形如 '2023-01-01 12:00:00' 的日期时间字符串
|
||||||
|
:return: 毫秒级时间戳
|
||||||
|
"""
|
||||||
|
dt = datetime.strptime(date_str, '%Y-%m-%d %H:%M:%S')
|
||||||
|
return int(dt.timestamp() * 1000)
|
||||||
|
|
||||||
|
def timestamp_to_datetime(timestamp: int) -> str:
|
||||||
|
"""
|
||||||
|
将时间戳转换为日期字符串
|
||||||
|
请考虑日期字符串位于北京时区
|
||||||
|
:param timestamp: 以毫秒为单位的时间戳
|
||||||
|
:return: 形如 '2023-01-01 12:00:00' 的日期时间字符串
|
||||||
|
"""
|
||||||
|
dt = datetime.fromtimestamp(timestamp / 1000, timezone(timedelta(hours=8)))
|
||||||
|
return dt.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
import logging
|
||||||
|
from time import sleep
|
||||||
|
from core.data_monitor import DataMonitor
|
||||||
|
from config import API_KEY, SECRET_KEY, PASSPHRASE, SANDBOX, MONITOR_CONFIG
|
||||||
|
|
||||||
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s: %(message)s')
|
||||||
|
|
||||||
|
|
||||||
|
class MonitorMain:
|
||||||
|
def __init__(self):
|
||||||
|
self.data_monitor = DataMonitor(
|
||||||
|
api_key=API_KEY,
|
||||||
|
secret_key=SECRET_KEY,
|
||||||
|
passphrase=PASSPHRASE,
|
||||||
|
sandbox=SANDBOX,
|
||||||
|
)
|
||||||
|
self.symbols = MONITOR_CONFIG.get("volume_monitor", {}).get("symbols", ["XCH-USDT"])
|
||||||
|
self.intervals = MONITOR_CONFIG.get("volume_monitor", {}).get("intervals", ["5m", "15m", "1H", "4H", "1D"])
|
||||||
|
self.initial_date = MONITOR_CONFIG.get("volume_monitor", {}).get("initial_date", "2025-07-01 00:00:00")
|
||||||
|
|
||||||
|
def initial_data(self):
|
||||||
|
for symbol in self.symbols:
|
||||||
|
for interval in self.intervals:
|
||||||
|
data = self.data_monitor.get_historical_kline_data(symbol=symbol,
|
||||||
|
start=self.initial_date,
|
||||||
|
bar=interval)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
monitor_main = MonitorMain()
|
||||||
|
monitor_main.initial_data()
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
# 量化工作计划
|
||||||
|
## 准备阶段
|
||||||
|
### API熟悉过程
|
||||||
|
1. 掌握API具体细节
|
||||||
|
- 虚拟货币价格查询
|
||||||
|
- 账户余额查询
|
||||||
|
- 现货交易买卖
|
||||||
|
- 永续合约买卖
|
||||||
|
- 设置杠杆
|
||||||
|
- 获得交易数据
|
||||||
|
### 实验阶段
|
||||||
|
1. 基于模拟盘实验
|
||||||
|
2. 设置支持永续合约权限
|
||||||
|
3. 完成现货买卖测试
|
||||||
|
4. 完成永续合约买平测试
|
||||||
|
5. 设置业务流程
|
||||||
|
- 开空单
|
||||||
|
- 卖现货
|
||||||
|
- 平空单
|
||||||
|
- 统计收益率
|
||||||
|
6. 进阶实验
|
||||||
|
- 获取对应货币技术指标
|
||||||
|
- 获取1分钟线KDJ, RSI,均线等技术形态
|
||||||
|
- 如果处于超买状态,以超买形态高点为参照物
|
||||||
|
- 之后连续三个周期,价格低于形态高点价格
|
||||||
|
- 此时方便做空,利于进行业务流程操作
|
||||||
|
6. 反复验证策略安全与完整性
|
||||||
|
### 实战阶段
|
||||||
|
|
||||||
8
play.py
8
play.py
|
|
@ -9,7 +9,7 @@ def main() -> None:
|
||||||
logging.info("=== 比特币量化交易系统 ===")
|
logging.info("=== 比特币量化交易系统 ===")
|
||||||
# 导入配置
|
# 导入配置
|
||||||
try:
|
try:
|
||||||
from config import API_KEY, SECRET_KEY, PASSPHRASE, TRADING_CONFIG, TIME_CONFIG
|
from config import API_KEY, SECRET_KEY, PASSPHRASE, SANDBOX, TRADING_CONFIG, TIME_CONFIG
|
||||||
except ImportError:
|
except ImportError:
|
||||||
logging.error("找不到config.py文件,请确保配置文件存在")
|
logging.error("找不到config.py文件,请确保配置文件存在")
|
||||||
return
|
return
|
||||||
|
|
@ -18,18 +18,18 @@ def main() -> None:
|
||||||
logging.error("请先在config.py中配置你的OKX API密钥!\n1. 登录OKX官网\n2. 进入API管理页面\n3. 创建API Key、Secret Key和Passphrase\n4. 将密钥填入config.py文件中的相应位置")
|
logging.error("请先在config.py中配置你的OKX API密钥!\n1. 登录OKX官网\n2. 进入API管理页面\n3. 创建API Key、Secret Key和Passphrase\n4. 将密钥填入config.py文件中的相应位置")
|
||||||
return
|
return
|
||||||
# 创建交易器实例
|
# 创建交易器实例
|
||||||
sandbox = TRADING_CONFIG.get("sandbox", True)
|
# sandbox = TRADING_CONFIG.get("sandbox", True)
|
||||||
symbol = TRADING_CONFIG.get("symbol", "BTC-USDT")
|
symbol = TRADING_CONFIG.get("symbol", "BTC-USDT")
|
||||||
position_size = TRADING_CONFIG.get("position_size", 0.001)
|
position_size = TRADING_CONFIG.get("position_size", 0.001)
|
||||||
trader = QuantTrader(
|
trader = QuantTrader(
|
||||||
API_KEY, SECRET_KEY, PASSPHRASE,
|
API_KEY, SECRET_KEY, PASSPHRASE,
|
||||||
sandbox=sandbox,
|
sandbox=SANDBOX,
|
||||||
symbol=symbol,
|
symbol=symbol,
|
||||||
position_size=position_size
|
position_size=position_size
|
||||||
)
|
)
|
||||||
strategy = QuantStrategy(
|
strategy = QuantStrategy(
|
||||||
API_KEY, SECRET_KEY, PASSPHRASE,
|
API_KEY, SECRET_KEY, PASSPHRASE,
|
||||||
sandbox=sandbox,
|
sandbox=SANDBOX,
|
||||||
symbol=symbol,
|
symbol=symbol,
|
||||||
position_size=position_size
|
position_size=position_size
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import logging
|
||||||
from time import sleep
|
from time import sleep
|
||||||
from core.base import QuantTrader
|
from core.base import QuantTrader
|
||||||
from core.strategy import QuantStrategy
|
from core.strategy import QuantStrategy
|
||||||
from config import API_KEY, SECRET_KEY, PASSPHRASE, TRADING_CONFIG, TIME_CONFIG
|
from config import API_KEY, SECRET_KEY, PASSPHRASE, SANDBOX, TRADING_CONFIG, TIME_CONFIG
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s: %(message)s')
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s %(levelname)s: %(message)s')
|
||||||
|
|
||||||
|
|
@ -12,7 +12,8 @@ class BizMain:
|
||||||
api_key = API_KEY
|
api_key = API_KEY
|
||||||
secret_key = SECRET_KEY
|
secret_key = SECRET_KEY
|
||||||
passphrase = PASSPHRASE
|
passphrase = PASSPHRASE
|
||||||
sandbox = TRADING_CONFIG.get("sandbox", True)
|
# sandbox = TRADING_CONFIG.get("sandbox", True)
|
||||||
|
sandbox = SANDBOX
|
||||||
symbol = TRADING_CONFIG.get("symbol", "BTC-USDT")
|
symbol = TRADING_CONFIG.get("symbol", "BTC-USDT")
|
||||||
position_size = TRADING_CONFIG.get("position_size", 0.001)
|
position_size = TRADING_CONFIG.get("position_size", 0.001)
|
||||||
self.trader = QuantTrader(api_key, secret_key, passphrase, sandbox, symbol, position_size)
|
self.trader = QuantTrader(api_key, secret_key, passphrase, sandbox, symbol, position_size)
|
||||||
Loading…
Reference in New Issue