144 lines
4.9 KiB
Python
144 lines
4.9 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
交易配置模块
|
|
"""
|
|
|
|
from typing import Dict, List
|
|
import threading
|
|
import json
|
|
import os
|
|
|
|
|
|
# 配置文件路径
|
|
CONFIG_FILE = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'data', 'trade_config.json')
|
|
|
|
|
|
class TradeConfig:
|
|
"""
|
|
交易配置单例
|
|
|
|
管理交易相关的配置参数,包括:
|
|
- 默认手数、止损偏移
|
|
- MT5时区偏移
|
|
- 品种配置(手数、止损偏移、关键点位等)
|
|
"""
|
|
_instance = None
|
|
_lock = threading.Lock()
|
|
|
|
def __init__(self):
|
|
self.enabled = True # 是否启用自动生成
|
|
|
|
# 默认配置
|
|
self.default_volume = 0.01 # 默认手数
|
|
self.default_sl_offset = 0.05 # 默认止损偏移(固定点数)
|
|
|
|
# MT5服务器时区偏移(单位:小时)
|
|
# 正数表示MT5时间比本地时间快,负数表示比本地时间慢
|
|
# 例如:MT5服务器时间是GMT+2,本地时间是GMT+8,则偏移为 -6
|
|
self.mt5_timezone_offset = 0
|
|
|
|
# 按品种配置: {symbol: {"volume": 0.01, "sl_offset": 0.05, "key_levels": "5000,5100", "key_level_threshold": 0.0008}}
|
|
self.symbol_config = {
|
|
"GOLD#": {"volume": 0.01, "sl_offset": 0.5},
|
|
"OILCASH#": {"volume": 0.01, "sl_offset": 0.05},
|
|
}
|
|
|
|
# 启动时自动加载配置文件
|
|
self._load_from_file()
|
|
|
|
def _load_from_file(self):
|
|
"""从配置文件加载配置"""
|
|
try:
|
|
if os.path.exists(CONFIG_FILE):
|
|
with open(CONFIG_FILE, 'r', encoding='utf-8') as f:
|
|
data = json.load(f)
|
|
self.update(data)
|
|
print(f"[TradeConfig] 已从配置文件加载: mt5_timezone_offset={self.mt5_timezone_offset}")
|
|
else:
|
|
print(f"[TradeConfig] 配置文件不存在: {CONFIG_FILE},使用默认配置")
|
|
except Exception as e:
|
|
print(f"[TradeConfig] 加载配置文件失败: {e},使用默认配置")
|
|
|
|
def save_to_file(self):
|
|
"""保存配置到文件"""
|
|
try:
|
|
os.makedirs(os.path.dirname(CONFIG_FILE), exist_ok=True)
|
|
with open(CONFIG_FILE, 'w', encoding='utf-8') as f:
|
|
json.dump(self.to_dict(), f, indent=2, ensure_ascii=False)
|
|
print(f"[TradeConfig] 配置已保存到: {CONFIG_FILE}")
|
|
return True
|
|
except Exception as e:
|
|
print(f"[TradeConfig] 保存配置文件失败: {e}")
|
|
return False
|
|
|
|
@classmethod
|
|
def get_instance(cls):
|
|
if cls._instance is None:
|
|
with cls._lock:
|
|
if cls._instance is None:
|
|
cls._instance = cls()
|
|
return cls._instance
|
|
|
|
def get_symbol_config(self, symbol: str) -> Dict:
|
|
"""获取品种配置,如果未配置则返回默认值"""
|
|
if symbol in self.symbol_config:
|
|
config = self.symbol_config[symbol]
|
|
return {
|
|
"volume": config.get("volume", self.default_volume),
|
|
"sl_offset": config.get("sl_offset", self.default_sl_offset),
|
|
"key_levels": config.get("key_levels", ""),
|
|
"key_level_threshold": config.get("key_level_threshold", 0.0008)
|
|
}
|
|
return {
|
|
"volume": self.default_volume,
|
|
"sl_offset": self.default_sl_offset,
|
|
"key_levels": "",
|
|
"key_level_threshold": 0.0008
|
|
}
|
|
|
|
def get_key_levels(self, symbol: str) -> List[float]:
|
|
"""
|
|
获取品种的关键点位列表
|
|
|
|
Args:
|
|
symbol: 品种名称
|
|
|
|
Returns:
|
|
关键点位列表,如 [5000, 5100, 5200]
|
|
"""
|
|
config = self.get_symbol_config(symbol)
|
|
key_levels_str = config.get("key_levels", "")
|
|
if not key_levels_str:
|
|
return []
|
|
|
|
levels = []
|
|
for level_str in key_levels_str.split(","):
|
|
level_str = level_str.strip()
|
|
if level_str:
|
|
try:
|
|
levels.append(float(level_str))
|
|
except ValueError:
|
|
continue
|
|
return sorted(levels)
|
|
|
|
def to_dict(self) -> Dict:
|
|
return {
|
|
"enabled": self.enabled,
|
|
"default_volume": self.default_volume,
|
|
"default_sl_offset": self.default_sl_offset,
|
|
"mt5_timezone_offset": self.mt5_timezone_offset,
|
|
"symbol_config": self.symbol_config
|
|
}
|
|
|
|
def update(self, data: Dict):
|
|
if "enabled" in data:
|
|
self.enabled = bool(data["enabled"])
|
|
if "default_volume" in data:
|
|
self.default_volume = float(data["default_volume"])
|
|
if "default_sl_offset" in data:
|
|
self.default_sl_offset = float(data["default_sl_offset"])
|
|
if "mt5_timezone_offset" in data:
|
|
self.mt5_timezone_offset = float(data["mt5_timezone_offset"])
|
|
if "symbol_config" in data:
|
|
self.symbol_config = data["symbol_config"] |