mirror of
https://github.com/silencesdg/mt5_python_ea_suite.git
synced 2026-07-31 12:47:44 +00:00
add files
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
/GEMINI.md
|
||||
/logs
|
||||
/myenv
|
||||
__pycache__/
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
import pandas as pd
|
||||
|
||||
class BacktestEngine:
|
||||
def __init__(self, df):
|
||||
"""
|
||||
df: 包含历史k线的DataFrame,至少包括open, high, low, close字段
|
||||
"""
|
||||
self.df = df
|
||||
|
||||
def run_strategy(self, strategy):
|
||||
"""
|
||||
执行策略的run_backtest,得到信号序列
|
||||
"""
|
||||
return strategy.run_backtest(self.df)
|
||||
|
||||
def combine_signals(self, signals_list, weights, buy_threshold, sell_threshold):
|
||||
"""
|
||||
多策略信号加权合成,并根据阈值生成最终信号
|
||||
返回合成信号序列
|
||||
"""
|
||||
df_signals = pd.concat(signals_list, axis=1).fillna(0)
|
||||
weighted_signals = df_signals * weights
|
||||
combined = weighted_signals.sum(axis=1)
|
||||
|
||||
def apply_threshold(score):
|
||||
if score >= buy_threshold:
|
||||
return 1
|
||||
elif score <= sell_threshold:
|
||||
return -1
|
||||
else:
|
||||
return 0
|
||||
|
||||
combined_signal = combined.apply(apply_threshold)
|
||||
return combined_signal
|
||||
|
||||
def calc_returns(self, signals):
|
||||
"""
|
||||
根据信号计算策略回测收益率(简化版)
|
||||
"""
|
||||
df = self.df.copy()
|
||||
df['signal'] = signals.shift(1).fillna(0) # 防止未来函数
|
||||
df['returns'] = df['close'].pct_change()
|
||||
df['strategy_returns'] = df['signal'] * df['returns']
|
||||
cum_ret = (1 + df['strategy_returns']).cumprod() - 1
|
||||
return cum_ret
|
||||
@@ -0,0 +1,24 @@
|
||||
from strategies import ma_cross, rsi, bollinger, mean_reversion, momentum_breakout, macd, kdj, turtle, daily_breakout, profit_protect, resilient_trend
|
||||
|
||||
SYMBOL = "XAUUSD"
|
||||
INTERVAL = 60 # 秒
|
||||
INITIAL_CAPITAL = 10000 # 初始资金
|
||||
|
||||
# 策略权重配置 (策略实例, 权重)
|
||||
STRATEGIES = [
|
||||
(ma_cross.Strategy(), 2.28),
|
||||
(rsi.Strategy(), 1.84),
|
||||
(bollinger.Strategy(), 1.46),
|
||||
(mean_reversion.Strategy(), 1.90),
|
||||
(momentum_breakout.Strategy(), 2.77),
|
||||
(macd.Strategy(), 1.70),
|
||||
(kdj.Strategy(), 0.65),
|
||||
(turtle.Strategy(), 0.68),
|
||||
(daily_breakout.Strategy(), 0.66),
|
||||
(profit_protect.Strategy(), 0.57),
|
||||
(resilient_trend.Strategy(), 1.0), # 新增带容错的趋势策略
|
||||
]
|
||||
|
||||
# 交易信号阈值
|
||||
BUY_THRESHOLD = 1.5
|
||||
SELL_THRESHOLD = -1.5
|
||||
@@ -0,0 +1,40 @@
|
||||
import logging
|
||||
import os
|
||||
|
||||
# 全局变量,用于存储logger实例
|
||||
_logger_instance = None
|
||||
|
||||
def setup_logger():
|
||||
global _logger_instance
|
||||
if _logger_instance:
|
||||
return _logger_instance
|
||||
|
||||
log_dir = "logs"
|
||||
os.makedirs(log_dir, exist_ok=True)
|
||||
log_file = os.path.join(log_dir, "strategy.log")
|
||||
|
||||
logger = logging.getLogger("StrategyLogger")
|
||||
logger.setLevel(logging.DEBUG)
|
||||
|
||||
# 防止重复添加handler
|
||||
if not logger.handlers:
|
||||
# 输出到控制台
|
||||
console_handler = logging.StreamHandler()
|
||||
console_handler.setLevel(logging.INFO)
|
||||
|
||||
# 输出到文件
|
||||
file_handler = logging.FileHandler(log_file, encoding="utf-8")
|
||||
file_handler.setLevel(logging.DEBUG)
|
||||
|
||||
formatter = logging.Formatter('%(asctime)s - [%(levelname)s] - %(message)s')
|
||||
console_handler.setFormatter(formatter)
|
||||
file_handler.setFormatter(formatter)
|
||||
|
||||
logger.addHandler(console_handler)
|
||||
logger.addHandler(file_handler)
|
||||
|
||||
_logger_instance = logger
|
||||
return logger
|
||||
|
||||
# 直接导出一个已经初始化好的logger实例
|
||||
logger = setup_logger()
|
||||
@@ -0,0 +1,90 @@
|
||||
import importlib
|
||||
import pandas as pd
|
||||
from utils import initialize, shutdown, get_rates, close_all, send_order
|
||||
from backtest import BacktestEngine
|
||||
from logger import logger
|
||||
from config import INITIAL_CAPITAL, STRATEGIES, BUY_THRESHOLD, SELL_THRESHOLD
|
||||
|
||||
# 导入优化器
|
||||
from optimizer import run_optimizer
|
||||
|
||||
def run_realtime():
|
||||
if not initialize():
|
||||
logger.error("MT5初始化失败")
|
||||
return
|
||||
|
||||
signals = []
|
||||
weights = []
|
||||
for strat, weight in STRATEGIES:
|
||||
try:
|
||||
logger.info(f"执行策略:{strat.__class__.__module__}")
|
||||
signal = strat.generate_signal()
|
||||
signals.append(signal)
|
||||
weights.append(weight)
|
||||
except Exception as e:
|
||||
logger.exception(f"运行策略 {strat.__class__.__module__} 时出错:{e}")
|
||||
|
||||
weighted_signal_sum = sum(s * w for s, w in zip(signals, weights))
|
||||
|
||||
if weighted_signal_sum >= BUY_THRESHOLD:
|
||||
logger.info(f"加权信号总和 ({weighted_signal_sum:.2f}) 达到买入阈值 ({BUY_THRESHOLD}),发送买入信号")
|
||||
close_all("XAUUSD")
|
||||
send_order("XAUUSD", 'buy')
|
||||
elif weighted_signal_sum <= SELL_THRESHOLD:
|
||||
logger.info(f"加权信号总和 ({weighted_signal_sum:.2f}) 达到卖出阈值 ({SELL_THRESHOLD}),发送卖出信号")
|
||||
close_all("XAUUSD")
|
||||
send_order("XAUUSD", 'sell')
|
||||
else:
|
||||
logger.info(f"加权信号总和 ({weighted_signal_sum:.2f}) 未达到交易阈值,无操作")
|
||||
|
||||
shutdown()
|
||||
|
||||
def run_backtest():
|
||||
if not initialize():
|
||||
logger.error("MT5初始化失败")
|
||||
return
|
||||
|
||||
symbol = "XAUUSD"
|
||||
timeframe = 1 # M1
|
||||
count = 50000
|
||||
rates = get_rates(symbol, timeframe, count)
|
||||
shutdown()
|
||||
|
||||
if rates is None:
|
||||
logger.error("获取历史数据失败")
|
||||
return
|
||||
|
||||
logger.info(f"初始资金: {INITIAL_CAPITAL}")
|
||||
|
||||
df = pd.DataFrame(rates)
|
||||
engine = BacktestEngine(df)
|
||||
signals_list = []
|
||||
weights = []
|
||||
|
||||
for strat, weight in STRATEGIES:
|
||||
try:
|
||||
logger.info(f"回测策略:{strat.__class__.__module__}")
|
||||
signals = engine.run_strategy(strat)
|
||||
signals_list.append(signals)
|
||||
weights.append(weight)
|
||||
except Exception as e:
|
||||
logger.exception(f"回测策略 {strat.__class__.__module__} 时出错:{e}")
|
||||
|
||||
combined_signal = engine.combine_signals(signals_list, weights, BUY_THRESHOLD, SELL_THRESHOLD)
|
||||
cum_ret = engine.calc_returns(combined_signal)
|
||||
final_capital = INITIAL_CAPITAL * (1 + cum_ret.iloc[-1])
|
||||
logger.info("策略组合回测完成")
|
||||
logger.info(f"最终资金: {final_capital:.2f}")
|
||||
logger.info(cum_ret.tail())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# --- 选择运行模式 ---
|
||||
# 1. 运行一次回测 (使用config.py中的默认权重)
|
||||
# run_backtest()
|
||||
|
||||
# 2. 运行实盘交易 (使用config.py中的默认权重)
|
||||
# run_realtime()
|
||||
|
||||
# 3. 运行遗传算法优化,寻找最佳权重
|
||||
run_optimizer()
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
|
||||
import random
|
||||
import numpy as np
|
||||
from deap import base, creator, tools, algorithms
|
||||
import multiprocessing
|
||||
|
||||
import pandas as pd
|
||||
from utils import initialize, shutdown, get_rates
|
||||
from backtest import BacktestEngine
|
||||
from logger import logger
|
||||
from config import INITIAL_CAPITAL, STRATEGIES, BUY_THRESHOLD, SELL_THRESHOLD
|
||||
|
||||
# 1. 定义适应度函数 (已优化)
|
||||
def evaluate_fitness(individual, df_data):
|
||||
"""
|
||||
评估函数现在接收预先加载的DataFrame作为参数,避免了重复IO。
|
||||
输入:
|
||||
- individual: 一个代表策略权重的列表。
|
||||
- df_data: 包含历史K线数据的Pandas DataFrame。
|
||||
输出: 一个元组,包含适应度分数(最终资金)。
|
||||
"""
|
||||
weights = individual
|
||||
|
||||
# 直接使用传入的df_data,不再需要get_rates
|
||||
engine = BacktestEngine(df_data)
|
||||
signals_list = []
|
||||
|
||||
strategy_instances = [s for s, w in STRATEGIES]
|
||||
|
||||
for strat in strategy_instances:
|
||||
signals = engine.run_strategy(strat)
|
||||
signals_list.append(signals)
|
||||
|
||||
combined_signal = engine.combine_signals(signals_list, weights, BUY_THRESHOLD, SELL_THRESHOLD)
|
||||
cum_ret = engine.calc_returns(combined_signal)
|
||||
final_capital = INITIAL_CAPITAL * (1 + cum_ret.iloc[-1])
|
||||
|
||||
# 在优化过程中,可以注释掉这行日志以提高速度,因为它会大量输出
|
||||
# logger.info(f"评估权重: {[f'{w:.2f}' for w in weights]} -> 最终资金: {final_capital:.2f}")
|
||||
|
||||
return (final_capital,)
|
||||
|
||||
# 2. 设置遗传算法 (已优化)
|
||||
def run_optimizer():
|
||||
"""
|
||||
配置并运行遗传算法
|
||||
"""
|
||||
# --- 数据预加载 ---
|
||||
logger.info("--- 开始遗传算法优化 --- ")
|
||||
logger.info("步骤 1/4: 初始化MT5并预加载历史数据...")
|
||||
if not initialize():
|
||||
logger.error("MT5初始化失败,无法开始优化")
|
||||
return
|
||||
|
||||
symbol = "XAUUSD"
|
||||
timeframe = 1 # M1
|
||||
count = 50000 # 使用与回测相同的数据量
|
||||
rates = get_rates(symbol, timeframe, count)
|
||||
shutdown() # 获取数据后即可关闭连接
|
||||
|
||||
if rates is None:
|
||||
logger.error("获取历史数据失败,优化终止")
|
||||
return
|
||||
|
||||
df_historical_data = pd.DataFrame(rates)
|
||||
logger.info(f"历史数据加载完成,共 {len(df_historical_data)} 条记录。")
|
||||
|
||||
# --- DEAP 设置 ---
|
||||
logger.info("步骤 2/4: 配置遗传算法...")
|
||||
creator.create("FitnessMax", base.Fitness, weights=(1.0,))
|
||||
creator.create("Individual", list, fitness=creator.FitnessMax)
|
||||
|
||||
toolbox = base.Toolbox()
|
||||
toolbox.register("attr_float", random.uniform, 0.1, 2.0)
|
||||
num_strategies = len(STRATEGIES)
|
||||
toolbox.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_float, n=num_strategies)
|
||||
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
|
||||
|
||||
toolbox.register("evaluate", evaluate_fitness, df_data=df_historical_data)
|
||||
toolbox.register("mate", tools.cxTwoPoint)
|
||||
toolbox.register("mutate", tools.mutGaussian, mu=0, sigma=0.5, indpb=0.2)
|
||||
toolbox.register("select", tools.selTournament, tournsize=3)
|
||||
|
||||
# --- 并行计算设置 ---
|
||||
logger.info("步骤 3/4: 配置并行计算和统计...")
|
||||
pool = multiprocessing.Pool()
|
||||
toolbox.register("map", pool.map)
|
||||
|
||||
# --- 统计功能设置 ---
|
||||
stats = tools.Statistics(lambda ind: ind.fitness.values)
|
||||
stats.register("avg", np.mean)
|
||||
stats.register("std", np.std)
|
||||
stats.register("min", np.min)
|
||||
stats.register("max", np.max)
|
||||
|
||||
# --- 运行算法 ---
|
||||
population = toolbox.population(n=50)
|
||||
ngen = 20
|
||||
cxpb = 0.5
|
||||
mutpb = 0.2
|
||||
|
||||
logger.info(f"步骤 4/4: 开始并行进化... (种群大小: {len(population)}, 进化代数: {ngen})")
|
||||
algorithms.eaSimple(population, toolbox, cxpb, mutpb, ngen, stats=stats, verbose=True)
|
||||
|
||||
pool.close()
|
||||
|
||||
# --- 结果 ---
|
||||
best_individual = tools.selBest(population, k=1)[0]
|
||||
best_fitness = best_individual.fitness.values[0]
|
||||
|
||||
logger.info("--- 遗传算法优化结束 ---")
|
||||
logger.info(f"找到的最佳权重: {[f'{w:.2f}' for w in best_individual]}")
|
||||
logger.info(f"对应的最佳最终资金: {best_fitness:.2f}")
|
||||
|
||||
return best_individual, best_fitness
|
||||
@@ -0,0 +1,3 @@
|
||||
MetaTrader5
|
||||
pandas
|
||||
deap
|
||||
@@ -0,0 +1,57 @@
|
||||
import MetaTrader5 as mt5
|
||||
import pandas as pd
|
||||
from utils import get_rates, close_all, send_order
|
||||
from logger import logger
|
||||
|
||||
class Strategy:
|
||||
def __init__(self):
|
||||
self.symbol = "XAUUSD"
|
||||
self.timeframe = mt5.TIMEFRAME_M1
|
||||
self.bollinger_period = 20
|
||||
self.bollinger_std_dev = 2
|
||||
|
||||
def _calculate_indicators(self, df):
|
||||
"""
|
||||
计算布林带指标
|
||||
"""
|
||||
mean = df['close'].rolling(self.bollinger_period).mean()
|
||||
std = df['close'].rolling(self.bollinger_period).std()
|
||||
df['upper_band'] = mean + self.bollinger_std_dev * std
|
||||
df['lower_band'] = mean - self.bollinger_std_dev * std
|
||||
return df
|
||||
|
||||
def generate_signal(self):
|
||||
"""
|
||||
布林带策略实盘:
|
||||
当价格跌破下轨买入,涨破上轨卖出。
|
||||
"""
|
||||
rates = get_rates(self.symbol, self.timeframe, self.bollinger_period + 30)
|
||||
if rates is None or len(rates) < self.bollinger_period:
|
||||
return 0
|
||||
df = pd.DataFrame(rates)
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
if df['close'].iloc[-2] < df['lower_band'].iloc[-2]:
|
||||
logger.info(f"价格跌破下轨,产生买入信号: {self.symbol}")
|
||||
return 1
|
||||
elif df['close'].iloc[-2] > df['upper_band'].iloc[-2]:
|
||||
logger.info(f"价格涨破上轨,产生卖出信号: {self.symbol}")
|
||||
return -1
|
||||
return 0
|
||||
|
||||
def run_backtest(self, df):
|
||||
"""
|
||||
布林带策略回测:
|
||||
价格突破下轨买入,突破上轨卖出
|
||||
返回信号序列:1买入,-1卖出,0无操作
|
||||
"""
|
||||
df = df.copy()
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
signals = pd.Series(0, index=df.index)
|
||||
for i in range(self.bollinger_period, len(df)):
|
||||
if df['close'].iloc[i-1] < df['lower_band'].iloc[i-1]:
|
||||
signals.iat[i] = 1
|
||||
elif df['close'].iloc[i-1] > df['upper_band'].iloc[i-1]:
|
||||
signals.iat[i] = -1
|
||||
return signals
|
||||
@@ -0,0 +1,68 @@
|
||||
import MetaTrader5 as mt5
|
||||
import pandas as pd
|
||||
from datetime import datetime
|
||||
from utils import get_rates, has_open_position, close_all, send_order
|
||||
from logger import logger
|
||||
|
||||
class Strategy:
|
||||
def __init__(self):
|
||||
self.symbol = "XAUUSD"
|
||||
self.timeframe = mt5.TIMEFRAME_M1
|
||||
|
||||
def _calculate_indicators(self, df):
|
||||
"""
|
||||
计算日内突破指标
|
||||
"""
|
||||
df['time'] = pd.to_datetime(df['time'], unit='s')
|
||||
today = datetime.now().date()
|
||||
day_data = df[df['time'].dt.date == today]
|
||||
if day_data.empty:
|
||||
return df, None, None
|
||||
day_high = day_data['high'].max()
|
||||
day_low = day_data['low'].min()
|
||||
return df, day_high, day_low
|
||||
|
||||
def generate_signal(self):
|
||||
"""
|
||||
日内突破策略实盘
|
||||
当价格突破当日最高买入,突破当日最低卖出
|
||||
"""
|
||||
rates = get_rates(self.symbol, self.timeframe, 1440) # 24 hours * 60 minutes
|
||||
if rates is None or len(rates) < 2:
|
||||
return 0
|
||||
df = pd.DataFrame(rates)
|
||||
df, day_high, day_low = self._calculate_indicators(df)
|
||||
|
||||
if day_high is None or day_low is None:
|
||||
return 0
|
||||
|
||||
if df['close'].iloc[-2] > day_high:
|
||||
logger.info(f"价格突破当日最高,产生买入信号: {self.symbol}")
|
||||
return 1
|
||||
elif df['close'].iloc[-2] < day_low:
|
||||
logger.info(f"价格突破当日最低,产生卖出信号: {self.symbol}")
|
||||
return -1
|
||||
return 0
|
||||
|
||||
def run_backtest(self, df):
|
||||
"""
|
||||
日内突破回测方法
|
||||
计算每个交易日的高低点,突破买卖信号
|
||||
"""
|
||||
df = df.copy()
|
||||
df['time'] = pd.to_datetime(df['time'], unit='s')
|
||||
|
||||
signals = pd.Series(0, index=df.index)
|
||||
|
||||
grouped = df.groupby(df['time'].dt.date)
|
||||
|
||||
for date, group in grouped:
|
||||
day_high = group['high'].max()
|
||||
day_low = group['low'].min()
|
||||
for i, row in group.iterrows():
|
||||
if row['close'] > day_high:
|
||||
signals.loc[i] = 1
|
||||
elif row['close'] < day_low:
|
||||
signals.loc[i] = -1
|
||||
|
||||
return signals
|
||||
@@ -0,0 +1,60 @@
|
||||
import MetaTrader5 as mt5
|
||||
import pandas as pd
|
||||
from utils import get_rates, has_open_position, close_all, send_order
|
||||
from logger import logger
|
||||
|
||||
|
||||
class Strategy:
|
||||
def __init__(self):
|
||||
self.symbol = "XAUUSD"
|
||||
self.timeframe = mt5.TIMEFRAME_M1
|
||||
self.kdj_period = 9
|
||||
self.kdj_buy_threshold = 10
|
||||
self.kdj_sell_threshold = 90
|
||||
|
||||
def _calculate_indicators(self, df):
|
||||
"""
|
||||
计算KDJ指标
|
||||
"""
|
||||
low_min = df['low'].rolling(self.kdj_period).min()
|
||||
high_max = df['high'].rolling(self.kdj_period).max()
|
||||
rsv = (df['close'] - low_min) / (high_max - low_min) * 100
|
||||
df['k'] = rsv.ewm(com=2).mean()
|
||||
df['d'] = df['k'].ewm(com=2).mean()
|
||||
df['j'] = 3 * df['k'] - 2 * df['d']
|
||||
return df
|
||||
|
||||
def generate_signal(self):
|
||||
"""
|
||||
KDJ策略实盘
|
||||
J值小于10买入,大于90卖出
|
||||
"""
|
||||
rates = get_rates(self.symbol, self.timeframe, self.kdj_period + 30)
|
||||
if rates is None or len(rates) < self.kdj_period:
|
||||
return 0
|
||||
df = pd.DataFrame(rates)
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
if df['j'].iloc[-2] < self.kdj_buy_threshold:
|
||||
logger.info(f"J值小于{self.kdj_buy_threshold},产生买入信号: {self.symbol}")
|
||||
return 1
|
||||
elif df['j'].iloc[-2] > self.kdj_sell_threshold:
|
||||
logger.info(f"J值大于{self.kdj_sell_threshold},产生卖出信号: {self.symbol}")
|
||||
return -1
|
||||
return 0
|
||||
|
||||
def run_backtest(self, df):
|
||||
"""
|
||||
KDJ回测方法
|
||||
根据J值极端生成信号
|
||||
"""
|
||||
df = df.copy()
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
signals = pd.Series(0, index=df.index)
|
||||
for i in range(self.kdj_period, len(df)):
|
||||
if df['j'].iloc[i-1] < self.kdj_buy_threshold:
|
||||
signals.iat[i] = 1
|
||||
elif df['j'].iloc[i-1] > self.kdj_sell_threshold:
|
||||
signals.iat[i] = -1
|
||||
return signals
|
||||
@@ -0,0 +1,56 @@
|
||||
import MetaTrader5 as mt5
|
||||
import pandas as pd
|
||||
from utils import get_rates, close_all, send_order
|
||||
from logger import logger
|
||||
|
||||
|
||||
class Strategy:
|
||||
def __init__(self):
|
||||
self.symbol = "XAUUSD"
|
||||
self.timeframe = mt5.TIMEFRAME_M1
|
||||
self.fast_ma_period = 5
|
||||
self.slow_ma_period = 20
|
||||
|
||||
def _calculate_indicators(self, df):
|
||||
"""
|
||||
计算技术指标
|
||||
"""
|
||||
df['fast_ma'] = df['close'].rolling(self.fast_ma_period).mean()
|
||||
df['slow_ma'] = df['close'].rolling(self.slow_ma_period).mean()
|
||||
return df
|
||||
|
||||
def generate_signal(self):
|
||||
"""
|
||||
均线交叉策略实盘:
|
||||
短期均线上穿长期均线买入,下穿卖出。
|
||||
"""
|
||||
rates = get_rates(self.symbol, self.timeframe, self.slow_ma_period + 30)
|
||||
if rates is None or len(rates) < self.slow_ma_period:
|
||||
return 0
|
||||
df = pd.DataFrame(rates)
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
if df['fast_ma'].iloc[-2] > df['slow_ma'].iloc[-2] and df['fast_ma'].iloc[-3] <= df['slow_ma'].iloc[-3]:
|
||||
logger.info(f"短期均线上穿长期均线,产生买入信号: {self.symbol}")
|
||||
return 1
|
||||
elif df['fast_ma'].iloc[-2] < df['slow_ma'].iloc[-2] and df['fast_ma'].iloc[-3] >= df['slow_ma'].iloc[-3]:
|
||||
logger.info(f"短期均线下穿长期均线,产生卖出信号: {self.symbol}")
|
||||
return -1
|
||||
return 0
|
||||
|
||||
def run_backtest(self, df):
|
||||
"""
|
||||
均线交叉回测:
|
||||
短期均线和长期均线交叉产生信号
|
||||
"""
|
||||
df = df.copy()
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
signals = pd.Series(0, index=df.index)
|
||||
# 从 slow_ma_period 开始循环,避免早期数据 NaN 问题
|
||||
for i in range(self.slow_ma_period, len(df)):
|
||||
if df['fast_ma'].iloc[i-1] > df['slow_ma'].iloc[i-1] and df['fast_ma'].iloc[i-2] <= df['slow_ma'].iloc[i-2]:
|
||||
signals.iat[i] = 1
|
||||
elif df['fast_ma'].iloc[i-1] < df['slow_ma'].iloc[i-1] and df['fast_ma'].iloc[i-2] >= df['slow_ma'].iloc[i-2]:
|
||||
signals.iat[i] = -1
|
||||
return signals
|
||||
@@ -0,0 +1,58 @@
|
||||
import MetaTrader5 as mt5
|
||||
import pandas as pd
|
||||
from utils import get_rates, has_open_position, close_all, send_order
|
||||
from logger import logger
|
||||
|
||||
|
||||
class Strategy:
|
||||
def __init__(self):
|
||||
self.symbol = "XAUUSD"
|
||||
self.timeframe = mt5.TIMEFRAME_M1
|
||||
self.fast_ema_period = 12
|
||||
self.slow_ema_period = 26
|
||||
self.signal_period = 9
|
||||
|
||||
def _calculate_indicators(self, df):
|
||||
"""
|
||||
计算MACD指标
|
||||
"""
|
||||
df['exp12'] = df['close'].ewm(span=self.fast_ema_period, adjust=False).mean()
|
||||
df['exp26'] = df['close'].ewm(span=self.slow_ema_period, adjust=False).mean()
|
||||
df['dif'] = df['exp12'] - df['exp26']
|
||||
df['dea'] = df['dif'].ewm(span=self.signal_period, adjust=False).mean()
|
||||
return df
|
||||
|
||||
def generate_signal(self):
|
||||
"""
|
||||
MACD策略实盘
|
||||
DIF线上穿DEA买入,反之卖出
|
||||
"""
|
||||
rates = get_rates(self.symbol, self.timeframe, self.slow_ema_period + self.signal_period + 30)
|
||||
if rates is None or len(rates) < self.slow_ema_period + self.signal_period:
|
||||
return 0
|
||||
df = pd.DataFrame(rates)
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
if df['dif'].iloc[-2] > df['dea'].iloc[-2] and df['dif'].iloc[-3] <= df['dea'].iloc[-3]:
|
||||
logger.info(f"DIF线上穿DEA,产生买入信号: {self.symbol}")
|
||||
return 1
|
||||
elif df['dif'].iloc[-2] < df['dea'].iloc[-2] and df['dif'].iloc[-3] >= df['dea'].iloc[-3]:
|
||||
logger.info(f"DIF线下穿DEA,产生卖出信号: {self.symbol}")
|
||||
return -1
|
||||
return 0
|
||||
|
||||
def run_backtest(self, df):
|
||||
"""
|
||||
MACD回测方法
|
||||
根据DIF和DEA金叉死叉生成信号
|
||||
"""
|
||||
df = df.copy()
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
signals = pd.Series(0, index=df.index)
|
||||
for i in range(2, len(df)):
|
||||
if df['dif'].iloc[i-1] > df['dea'].iloc[i-1] and df['dif'].iloc[i-2] <= df['dea'].iloc[i-2]:
|
||||
signals.iat[i] = 1
|
||||
elif df['dif'].iloc[i-1] < df['dea'].iloc[i-1] and df['dif'].iloc[i-2] >= df['dea'].iloc[i-2]:
|
||||
signals.iat[i] = -1
|
||||
return signals
|
||||
@@ -0,0 +1,57 @@
|
||||
import MetaTrader5 as mt5
|
||||
import pandas as pd
|
||||
from utils import get_rates, close_all, send_order
|
||||
from logger import logger
|
||||
|
||||
|
||||
class Strategy:
|
||||
def __init__(self):
|
||||
self.symbol = "XAUUSD"
|
||||
self.timeframe = mt5.TIMEFRAME_M1
|
||||
self.mean_reversion_period = 20
|
||||
self.mean_reversion_std_dev = 2
|
||||
|
||||
def _calculate_indicators(self, df):
|
||||
"""
|
||||
计算均值回归指标
|
||||
"""
|
||||
mean = df['close'].rolling(self.mean_reversion_period).mean()
|
||||
std = df['close'].rolling(self.mean_reversion_period).std()
|
||||
df['upper_band'] = mean + self.mean_reversion_std_dev * std
|
||||
df['lower_band'] = mean - self.mean_reversion_std_dev * std
|
||||
return df
|
||||
|
||||
def generate_signal(self):
|
||||
"""
|
||||
均值回归策略实盘:
|
||||
当价格超过20日均线正负2个标准差买卖。
|
||||
"""
|
||||
rates = get_rates(self.symbol, self.timeframe, self.mean_reversion_period + 30)
|
||||
if rates is None or len(rates) < self.mean_reversion_period:
|
||||
return 0
|
||||
df = pd.DataFrame(rates)
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
if df['close'].iloc[-2] > df['upper_band'].iloc[-2]:
|
||||
logger.info(f"价格超过上轨,产生卖出信号: {self.symbol}")
|
||||
return -1
|
||||
elif df['close'].iloc[-2] < df['lower_band'].iloc[-2]:
|
||||
logger.info(f"价格低于下轨,产生买入信号: {self.symbol}")
|
||||
return 1
|
||||
return 0
|
||||
|
||||
def run_backtest(self, df):
|
||||
"""
|
||||
均值回归回测:
|
||||
价格突破上下轨卖出/买入
|
||||
"""
|
||||
df = df.copy()
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
signals = pd.Series(0, index=df.index)
|
||||
for i in range(self.mean_reversion_period, len(df)):
|
||||
if df['close'].iloc[i-1] > df['upper_band'].iloc[i-1]:
|
||||
signals.iat[i] = -1
|
||||
elif df['close'].iloc[i-1] < df['lower_band'].iloc[i-1]:
|
||||
signals.iat[i] = 1
|
||||
return signals
|
||||
@@ -0,0 +1,54 @@
|
||||
import MetaTrader5 as mt5
|
||||
import pandas as pd
|
||||
from utils import get_rates, has_open_position, close_all, send_order
|
||||
from logger import logger
|
||||
|
||||
|
||||
class Strategy:
|
||||
def __init__(self):
|
||||
self.symbol = "XAUUSD"
|
||||
self.timeframe = mt5.TIMEFRAME_M1
|
||||
self.breakout_period = 20
|
||||
|
||||
def _calculate_indicators(self, df):
|
||||
"""
|
||||
计算动量突破指标
|
||||
"""
|
||||
df['high_20'] = df['high'].rolling(self.breakout_period).max()
|
||||
df['low_20'] = df['low'].rolling(self.breakout_period).min()
|
||||
return df
|
||||
|
||||
def generate_signal(self):
|
||||
"""
|
||||
动量突破策略实盘
|
||||
价格突破过去20根K线最高点买入,突破最低点卖出
|
||||
"""
|
||||
rates = get_rates(self.symbol, self.timeframe, self.breakout_period + 30)
|
||||
if rates is None or len(rates) < self.breakout_period:
|
||||
return 0
|
||||
df = pd.DataFrame(rates)
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
if df['close'].iloc[-2] > df['high_20'].iloc[-3]:
|
||||
logger.info(f"价格突破{self.breakout_period}日最高点,产生买入信号: {self.symbol}")
|
||||
return 1
|
||||
elif df['close'].iloc[-2] < df['low_20'].iloc[-3]:
|
||||
logger.info(f"价格突破{self.breakout_period}日最低点,产生卖出信号: {self.symbol}")
|
||||
return -1
|
||||
return 0
|
||||
|
||||
def run_backtest(self, df):
|
||||
"""
|
||||
动量突破回测方法
|
||||
过去20根K线最高最低突破生成买卖信号
|
||||
"""
|
||||
df = df.copy()
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
signals = pd.Series(0, index=df.index)
|
||||
for i in range(self.breakout_period, len(df)):
|
||||
if df['close'].iloc[i-1] > df['high_20'].iloc[i-2]:
|
||||
signals.iat[i] = 1
|
||||
elif df['close'].iloc[i-1] < df['low_20'].iloc[i-2]:
|
||||
signals.iat[i] = -1
|
||||
return signals
|
||||
@@ -0,0 +1,79 @@
|
||||
|
||||
import pandas as pd
|
||||
from logger import logger
|
||||
|
||||
class Strategy:
|
||||
def __init__(self):
|
||||
self.symbol = "XAUUSD"
|
||||
# --- 策略核心参数 ---
|
||||
# 固定止损线:亏损10%则卖出
|
||||
self.stop_loss_pct = -0.10
|
||||
# 利润回撤百分比:从最高利润点回撤30%则卖出
|
||||
self.profit_retracement_pct = 0.30
|
||||
# 追踪止损的激活阈值:当利润超过5%后,才开始启动追踪止损逻辑
|
||||
self.min_profit_for_trailing = 0.05
|
||||
|
||||
def generate_signal(self):
|
||||
"""
|
||||
此策略为资金管理和退出策略,不产生独立的买入信号。
|
||||
实盘逻辑应与其他策略结合,此处仅为框架完整性。
|
||||
"""
|
||||
logger.warning("ProfitProtect策略是一个退出策略,不应单独用于实盘产生信号。")
|
||||
return 0
|
||||
|
||||
def run_backtest(self, df):
|
||||
"""
|
||||
盈利保护策略回测:
|
||||
- 固定止损:亏损10%卖出。
|
||||
- 追踪止损:利润超过5%后启动,从最高利润点回撤30%卖出。
|
||||
为了独立回测,本策略会在一开始买入,然后应用退出逻辑。
|
||||
"""
|
||||
df = df.copy()
|
||||
signals = pd.Series(0, index=df.index)
|
||||
|
||||
if len(df) < 2:
|
||||
return signals
|
||||
|
||||
# --- 回测状态变量 ---
|
||||
position_open = False
|
||||
entry_price = 0.0
|
||||
peak_profit_pct = 0.0 # 记录达到的最高利润百分比
|
||||
|
||||
for i in range(len(df)):
|
||||
# 如果没有持仓,就在第一个机会买入(用于独立回测)
|
||||
if not position_open:
|
||||
position_open = True
|
||||
entry_price = df['close'].iloc[i]
|
||||
signals.iat[i] = 1 # 买入信号
|
||||
peak_profit_pct = 0.0 # 重置最高利润
|
||||
continue
|
||||
|
||||
# 如果有持仓,则执行退出逻辑
|
||||
if position_open:
|
||||
current_price = df['close'].iloc[i]
|
||||
current_profit_pct = (current_price - entry_price) / entry_price
|
||||
|
||||
# 1. 更新最高利润点
|
||||
peak_profit_pct = max(peak_profit_pct, current_profit_pct)
|
||||
|
||||
# 2. 检查固定止损条件
|
||||
if current_profit_pct <= self.stop_loss_pct:
|
||||
logger.info(f"索引 {i}: 触发固定止损。入场价: {entry_price:.2f}, 当前价: {current_price:.2f}, 亏损: {current_profit_pct:.2%}")
|
||||
signals.iat[i] = -1 # 卖出信号
|
||||
position_open = False # 平仓
|
||||
continue
|
||||
|
||||
# 3. 检查追踪止损条件
|
||||
# 只有当最高利润超过了激活阈值,才开始计算回撤
|
||||
if peak_profit_pct > self.min_profit_for_trailing:
|
||||
retracement_from_peak = (peak_profit_pct - current_profit_pct)
|
||||
|
||||
# 避免除以零或负数的情况
|
||||
if peak_profit_pct > 0:
|
||||
retracement_pct = retracement_from_peak / peak_profit_pct
|
||||
if retracement_pct >= self.profit_retracement_pct:
|
||||
logger.info(f"索引 {i}: 触发追踪止损。最高利润: {peak_profit_pct:.2%}, 当前利润: {current_profit_pct:.2%}, 回撤超过30%")
|
||||
signals.iat[i] = -1 # 卖出信号
|
||||
position_open = False # 平仓
|
||||
continue
|
||||
return signals
|
||||
@@ -0,0 +1,113 @@
|
||||
|
||||
import pandas as pd
|
||||
from logger import logger
|
||||
from utils import get_rates
|
||||
|
||||
class Strategy:
|
||||
def __init__(self):
|
||||
self.symbol = "XAUUSD"
|
||||
# --- 策略核心参数 ---
|
||||
self.trend_period = 50
|
||||
self.retracement_tolerance = 0.30
|
||||
|
||||
# --- 策略状态变量 ---
|
||||
self.current_trend = "none" # none, uptrend, downtrend
|
||||
self.trend_peak = 0.0 # 上升趋势中的最高价
|
||||
self.trend_trough = float('inf') # 下降趋势中的最低价
|
||||
|
||||
def generate_signal(self):
|
||||
"""
|
||||
带状态维护的实盘信号生成方法。
|
||||
"""
|
||||
# 获取足够的数据来计算滚动高低点
|
||||
rates = get_rates(self.symbol, 1, self.trend_period + 5)
|
||||
if rates is None or len(rates) < self.trend_period:
|
||||
return 0 # 数据不足,不产生信号
|
||||
|
||||
df = pd.DataFrame(rates)
|
||||
|
||||
# 获取当前价格和用于判断突破的历史高低点
|
||||
current_price = df['close'].iloc[-1]
|
||||
high_period = df['high'].rolling(self.trend_period).max().iloc[-2]
|
||||
low_period = df['low'].rolling(self.trend_period).min().iloc[-2]
|
||||
|
||||
signal = 0
|
||||
|
||||
# 状态 1: 当前无趋势,等待趋势开始
|
||||
if self.current_trend == "none":
|
||||
if current_price > high_period:
|
||||
self.current_trend = "uptrend"
|
||||
self.trend_peak = current_price
|
||||
signal = 1
|
||||
logger.info(f"实盘: 突破进入上升趋势,买入价: {current_price:.2f}")
|
||||
elif current_price < low_period:
|
||||
self.current_trend = "downtrend"
|
||||
self.trend_trough = current_price
|
||||
signal = -1
|
||||
logger.info(f"实盘: 跌破进入下降趋势,卖出价: {current_price:.2f}")
|
||||
|
||||
# 状态 2: 当前处于上升趋势
|
||||
elif self.current_trend == "uptrend":
|
||||
if current_price < self.trend_peak * (1 - self.retracement_tolerance):
|
||||
logger.info(f"实盘: 上升趋势结束。最高点: {self.trend_peak:.2f}, 当前价: {current_price:.2f}。平仓卖出。")
|
||||
signal = -1
|
||||
self.current_trend = "none" # 重置状态
|
||||
else:
|
||||
self.trend_peak = max(self.trend_peak, current_price)
|
||||
|
||||
# 状态 3: 当前处于下降趋势
|
||||
elif self.current_trend == "downtrend":
|
||||
if current_price > self.trend_trough * (1 + self.retracement_tolerance):
|
||||
logger.info(f"实盘: 下降趋势结束。最低点: {self.trend_trough:.2f}, 当前价: {current_price:.2f}。平仓买入。")
|
||||
signal = 1
|
||||
self.current_trend = "none" # 重置状态
|
||||
else:
|
||||
self.trend_trough = min(self.trend_trough, current_price)
|
||||
|
||||
return signal
|
||||
|
||||
def run_backtest(self, df):
|
||||
"""
|
||||
带容错的趋势跟踪策略回测:
|
||||
- 突破N周期高点,进入上升趋势,回撤30%则趋势结束。
|
||||
- 跌破N周期低点,进入下降趋势,反弹30%则趋势结束。
|
||||
"""
|
||||
df = df.copy()
|
||||
signals = pd.Series(0, index=df.index)
|
||||
|
||||
df['high_period'] = df['high'].rolling(self.trend_period).max().shift(1)
|
||||
df['low_period'] = df['low'].rolling(self.trend_period).min().shift(1)
|
||||
|
||||
# 回测时使用局部变量来管理状态,避免干扰实盘状态
|
||||
backtest_trend = "none"
|
||||
backtest_peak = 0.0
|
||||
backtest_trough = float('inf')
|
||||
|
||||
for i in range(self.trend_period, len(df)):
|
||||
current_price = df['close'].iloc[i]
|
||||
|
||||
if backtest_trend == "none":
|
||||
if current_price > df['high_period'].iloc[i]:
|
||||
backtest_trend = "uptrend"
|
||||
backtest_peak = current_price
|
||||
signals.iat[i] = 1
|
||||
elif current_price < df['low_period'].iloc[i]:
|
||||
backtest_trend = "downtrend"
|
||||
backtest_trough = current_price
|
||||
signals.iat[i] = -1
|
||||
|
||||
elif backtest_trend == "uptrend":
|
||||
if current_price < backtest_peak * (1 - self.retracement_tolerance):
|
||||
signals.iat[i] = -1
|
||||
backtest_trend = "none"
|
||||
else:
|
||||
backtest_peak = max(backtest_peak, current_price)
|
||||
|
||||
elif backtest_trend == "downtrend":
|
||||
if current_price > backtest_trough * (1 + self.retracement_tolerance):
|
||||
signals.iat[i] = 1
|
||||
backtest_trend = "none"
|
||||
else:
|
||||
backtest_trough = min(backtest_trough, current_price)
|
||||
|
||||
return signals
|
||||
@@ -0,0 +1,59 @@
|
||||
import MetaTrader5 as mt5
|
||||
import pandas as pd
|
||||
from utils import get_rates, close_all, send_order
|
||||
from logger import logger
|
||||
|
||||
|
||||
class Strategy:
|
||||
def __init__(self):
|
||||
self.symbol = "XAUUSD"
|
||||
self.timeframe = mt5.TIMEFRAME_M1
|
||||
self.rsi_period = 14
|
||||
self.rsi_buy_threshold = 30
|
||||
self.rsi_sell_threshold = 70
|
||||
|
||||
def _calculate_indicators(self, df):
|
||||
"""
|
||||
计算RSI指标
|
||||
"""
|
||||
delta = df['close'].diff()
|
||||
gain = delta.where(delta > 0, 0).rolling(self.rsi_period).mean()
|
||||
loss = -delta.where(delta < 0, 0).rolling(self.rsi_period).mean()
|
||||
rs = gain / loss
|
||||
df['rsi'] = 100 - (100 / (1 + rs))
|
||||
return df
|
||||
|
||||
def generate_signal(self):
|
||||
"""
|
||||
RSI策略实盘:
|
||||
RSI < 30买入,RSI > 70卖出。
|
||||
"""
|
||||
rates = get_rates(self.symbol, self.timeframe, self.rsi_period + 30)
|
||||
if rates is None or len(rates) < self.rsi_period:
|
||||
return 0
|
||||
df = pd.DataFrame(rates)
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
if df['rsi'].iloc[-2] < self.rsi_buy_threshold:
|
||||
logger.info(f"RSI小于{self.rsi_buy_threshold},产生买入信号: {self.symbol}")
|
||||
return 1
|
||||
elif df['rsi'].iloc[-2] > self.rsi_sell_threshold:
|
||||
logger.info(f"RSI大于{self.rsi_sell_threshold},产生卖出信号: {self.symbol}")
|
||||
return -1
|
||||
return 0
|
||||
|
||||
def run_backtest(self, df):
|
||||
"""
|
||||
RSI回测:
|
||||
RSI < 30买入,RSI > 70卖出。
|
||||
"""
|
||||
df = df.copy()
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
signals = pd.Series(0, index=df.index)
|
||||
for i in range(self.rsi_period, len(df)):
|
||||
if df['rsi'].iloc[i-1] < self.rsi_buy_threshold:
|
||||
signals.iat[i] = 1
|
||||
elif df['rsi'].iloc[i-1] > self.rsi_sell_threshold:
|
||||
signals.iat[i] = -1
|
||||
return signals
|
||||
@@ -0,0 +1,54 @@
|
||||
import MetaTrader5 as mt5
|
||||
import pandas as pd
|
||||
from utils import get_rates, has_open_position, close_all, send_order
|
||||
from logger import logger
|
||||
|
||||
|
||||
class Strategy:
|
||||
def __init__(self):
|
||||
self.symbol = "XAUUSD"
|
||||
self.timeframe = mt5.TIMEFRAME_M1
|
||||
self.turtle_period = 20
|
||||
|
||||
def _calculate_indicators(self, df):
|
||||
"""
|
||||
计算海龟交易指标
|
||||
"""
|
||||
df['high_20'] = df['high'].rolling(self.turtle_period).max()
|
||||
df['low_20'] = df['low'].rolling(self.turtle_period).min()
|
||||
return df
|
||||
|
||||
def generate_signal(self):
|
||||
"""
|
||||
海龟交易策略实盘
|
||||
20日最高突破买入,20日最低突破卖出
|
||||
"""
|
||||
rates = get_rates(self.symbol, self.timeframe, self.turtle_period + 30)
|
||||
if rates is None or len(rates) < self.turtle_period:
|
||||
return 0
|
||||
df = pd.DataFrame(rates)
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
if df['close'].iloc[-2] > df['high_20'].iloc[-3]:
|
||||
logger.info(f"价格突破{self.turtle_period}日最高点,产生买入信号: {self.symbol}")
|
||||
return 1
|
||||
elif df['close'].iloc[-2] < df['low_20'].iloc[-3]:
|
||||
logger.info(f"价格突破{self.turtle_period}日最低点,产生卖出信号: {self.symbol}")
|
||||
return -1
|
||||
return 0
|
||||
|
||||
def run_backtest(self, df):
|
||||
"""
|
||||
海龟交易回测方法
|
||||
根据20日高低突破生成买卖信号
|
||||
"""
|
||||
df = df.copy()
|
||||
df = self._calculate_indicators(df)
|
||||
|
||||
signals = pd.Series(0, index=df.index)
|
||||
for i in range(self.turtle_period, len(df)):
|
||||
if df['close'].iloc[i-1] > df['high_20'].iloc[i-2]:
|
||||
signals.iat[i] = 1
|
||||
elif df['close'].iloc[i-1] < df['low_20'].iloc[i-2]:
|
||||
signals.iat[i] = -1
|
||||
return signals
|
||||
@@ -0,0 +1,70 @@
|
||||
import MetaTrader5 as mt5
|
||||
import pandas as pd
|
||||
from logger import setup_logger
|
||||
logger = setup_logger()
|
||||
|
||||
def initialize():
|
||||
if not mt5.initialize():
|
||||
logger.error("MT5初始化失败,错误代码:%d", mt5.last_error())
|
||||
return False
|
||||
return True
|
||||
|
||||
def shutdown():
|
||||
mt5.shutdown()
|
||||
|
||||
def get_rates(symbol, timeframe, count):
|
||||
rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, count)
|
||||
if rates is None:
|
||||
logger.info(f"获取{symbol}历史数据失败")
|
||||
return None
|
||||
return rates
|
||||
|
||||
def has_open_position(symbol):
|
||||
positions = mt5.positions_get(symbol=symbol)
|
||||
return positions is not None and len(positions) > 0
|
||||
|
||||
def close_all(symbol):
|
||||
positions = mt5.positions_get(symbol=symbol)
|
||||
if positions is None:
|
||||
return
|
||||
for pos in positions:
|
||||
request = {
|
||||
"action": mt5.TRADE_ACTION_DEAL,
|
||||
"position": pos.ticket,
|
||||
"symbol": symbol,
|
||||
"volume": pos.volume,
|
||||
"type": mt5.ORDER_TYPE_SELL if pos.type == 0 else mt5.ORDER_TYPE_BUY,
|
||||
"price": mt5.symbol_info_tick(symbol).bid if pos.type == 0 else mt5.symbol_info_tick(symbol).ask,
|
||||
"deviation": 20,
|
||||
"magic": 234000,
|
||||
"comment": "Close position",
|
||||
"type_filling": mt5.ORDER_FILLING_RETURN,
|
||||
}
|
||||
mt5.order_send(request)
|
||||
|
||||
def send_order(symbol, order_type, volume=0.01):
|
||||
symbol_info_tick = mt5.symbol_info_tick(symbol)
|
||||
if symbol_info_tick is None:
|
||||
logger.error(f"无法获取{symbol}行情")
|
||||
return
|
||||
|
||||
price = symbol_info_tick.ask if order_type == "buy" else symbol_info_tick.bid
|
||||
order_type_mt5 = mt5.ORDER_TYPE_BUY if order_type == "buy" else mt5.ORDER_TYPE_SELL
|
||||
|
||||
request = {
|
||||
"action": mt5.TRADE_ACTION_DEAL,
|
||||
"symbol": symbol,
|
||||
"volume": volume,
|
||||
"type": order_type_mt5,
|
||||
"price": price,
|
||||
"deviation": 20,
|
||||
"magic": 234000,
|
||||
"comment": f"{order_type} order",
|
||||
"type_filling": mt5.ORDER_FILLING_RETURN,
|
||||
}
|
||||
|
||||
result = mt5.order_send(request)
|
||||
if result.retcode != mt5.TRADE_RETCODE_DONE:
|
||||
logger.error(f"下单失败,retcode={result.retcode}")
|
||||
else:
|
||||
logger.info(f"下单成功: {order_type} {symbol} {volume}")
|
||||
Reference in New Issue
Block a user