# 克隆自聚宽文章：https://www.joinquant.com/post/67272
# 标题：七星高照ETF轮动优化
# 作者：jinshen0901

# 克隆自聚宽文章：https://www.joinquant.com/post/67252
# 标题：七星高照4.0最小化过拟合ETF轮动，收益还能看。
# 作者：弈剑
# 优化版本：降低换手率，增加调仓阈值、最小持仓天数、止损冷却等

import numpy as np
import math
import pandas as pd
from jqdata import *

# ================== 【全局静态常量】==================

ETF_POOL_DEF = [
    # 境外
    "513100.XSHG", #纳指ETF
    "513500.XSHG", #标普500ETF
    "513520.XSHG", #日经ETF
    "513030.XSHG", #德国ETF
    "513080.XSHG", #法国ETF
    "159100.XSHE", #巴西ETF
    "159329.XSHE", #沙特ETF
    # 商品
    "518880.XSHG", #黄金ETF
    "159980.XSHE", #有色ETF
    "159985.XSHE", #豆粕ETF
    "159981.XSHE", #能源化工ETF
    "513350.XSHG", #油气ETF
    # 债券
    "511090.XSHG", #30年国债ETF
    # 国内
    "513130.XSHG", #恒生科技ETF
    #"513690.XSHG", #港股红利ETF
    "159915.XSHE", #创业板ETF
    "563300.XSHG", #中证2000ETF
    "510310.XSHG", #300ETF
    "588220.XSHG", #科创100ETF
]

# ============== 策略参数默认值（_DEF后缀） ==============

# 动量计算参数
HOLDINGS_NUM_DEF = 1               # 持仓ETF数量
LOOKBACK_DAYS_DEF = 24             # 长期动量计算周期
DEFENSIVE_ETF_DEF = "511880.XSHG"  # 防御性ETF（货币ETF）
MIN_MONEY_DEF = 5000               # 最小交易金额

# 风险控制参数
STOP_LOSS_DEF = 0.95               # 固定百分比止损线（下跌5%止损）
LOSS_DEF = 0.95                  # 近3日跌幅止损线

# 成交量过滤参数
ENABLE_VOLUME_CHECK_DEF = True     # 是否启用成交量过滤
VOLUME_LOOKBACK_DEF = 5            # 成交量历史参考天数
VOLUME_THRESHOLD_DEF = 2.5         # 放量阈值（大于设定值视为放量）
VOLUME_RETURN_LIMIT_DEF = 1        # 年化收益率过滤阈值

# R²筛选参数
USE_R2_FILTER_DEF = True           # 是否启用R²筛选
R2_MIN_THRESHOLD_DEF = 0.4         # R²最低阈值（0.3≤R²≤1）

# 得分阈值
MIN_SCORE_THRESHOLD_DEF = 0.0      # 最低得分阈值
MAX_SCORE_THRESHOLD_DEF = 5.0      # 最高得分阈值

# ============== 新增优化参数 ==============
TRADE_FREQUENCY_DEF = 'daily'          # 调仓频率：'daily'每日, 'weekly'每周, 'biweekly'每两周
SCORE_DIFF_THRESHOLD_DEF = 0.05         # 换仓得分差阈值（5%）
MIN_HOLDING_DAYS_DEF = 1                # 最小持仓天数（止损除外）
STOP_LOSS_COOLDOWN_DEF = 1              # 止损后冷却天数（交易日）

# =================== 【初始化函数】 =====================

def initialize(context):
    
    g.context = context
    
    # ============== 赋值全局常量到g变量 ==============
    g.etf_pool = ETF_POOL_DEF  # 引用全局ETF池常量
    
    # 设置日志级别
    log.set_level('order', 'error')
    log.set_level('system', 'error')
    log.set_level('strategy', 'info')
    
    # ================ 聚宽环境初始化 =================
    set_option("avoid_future_data", True)
    set_option("use_real_price", True)
    set_slippage(PriceRelatedSlippage(0.0001), type="fund")
    set_order_cost(
        OrderCost(
            open_tax=0,
            close_tax=0,
            open_commission=0.0002,
            close_commission=0.0002,
            close_today_commission=0,
            min_commission=5,
        ),
        type="fund",
    )
    set_benchmark("000300.XSHG")  
    
    # ===== 赋值策略参数到g变量 =====
    # 原有参数
    g.lookback_days = LOOKBACK_DAYS_DEF
    g.holdings_num = HOLDINGS_NUM_DEF
    g.defensive_etf = DEFENSIVE_ETF_DEF
    g.min_money = MIN_MONEY_DEF
    g.stop_loss = STOP_LOSS_DEF
    g.loss = LOSS_DEF
    g.enable_volume_check = ENABLE_VOLUME_CHECK_DEF
    g.volume_lookback = VOLUME_LOOKBACK_DEF
    g.volume_threshold = VOLUME_THRESHOLD_DEF
    g.volume_return_limit = VOLUME_RETURN_LIMIT_DEF
    g.use_r2_filter = USE_R2_FILTER_DEF
    g.r2_min_threshold = R2_MIN_THRESHOLD_DEF
    g.min_score_threshold = MIN_SCORE_THRESHOLD_DEF
    g.max_score_threshold = MAX_SCORE_THRESHOLD_DEF

    # 新增优化参数
    g.trade_frequency = TRADE_FREQUENCY_DEF
    g.score_diff_threshold = SCORE_DIFF_THRESHOLD_DEF
    g.min_holding_days = MIN_HOLDING_DAYS_DEF
    g.stop_loss_cooldown = STOP_LOSS_COOLDOWN_DEF

    # ================ 状态变量 ================
    g.positions = {}                     # 记录持仓（实际未使用，保留）
    g.last_trade_date = None             # 上次调仓日期
    g.last_ranked_etfs = None            # 上次计算的排名结果（缓存）
    g.stop_loss_record = {}               # 止损记录 {security: 止损日期}
    g.holding_start_date = {}             # 持仓开始日期 {security: 买入日期}
   
    # ================ 交易调度 ================
    run_daily(check_positions, time='09:25')
    run_daily(etf_sell_trade, time='13:10')
    run_daily(etf_buy_trade, time='13:11')
    
    log.info(f"""策略参数初始化完成:
    - ETF池大小: {len(g.etf_pool)} 只ETF | 动量周期: {g.lookback_days} 天 | 成交量过滤: {'启用' if g.enable_volume_check else '禁用'} | 防御ETF: {g.defensive_etf}
    - 新增参数: 调仓频率={g.trade_frequency} | 换仓阈值={g.score_diff_threshold*100}% | 最小持仓天数={g.min_holding_days} | 止损冷却={g.stop_loss_cooldown}天
""")

# ============ 持仓检查 ===============
def check_positions(context):
    current_data = get_current_data()
    for security in context.portfolio.positions:
        position = context.portfolio.positions[security]
        if position.total_amount > 0:
            security_name = get_security_name(security)
            log.debug(f"📊 持仓检查: {security} {security_name}, 数量: {position.total_amount}, 成本: {position.avg_cost:.3f}, 当前价: {position.price:.3f}")
            if current_data[security].paused:
                log.info(f"⚠️ {security} {security_name} 今日停牌")

# ==================== 判断是否调仓日 ====================
def is_trade_day(context):
    """根据g.trade_frequency判断今日是否为调仓日"""
    if g.trade_frequency == 'daily':
        return True
    current_date = context.current_dt.date()
    if g.last_trade_date is None:
        return True  # 首次运行
    # 计算上次调仓至今的天数（自然日，不考虑交易日）
    days_diff = (current_date - g.last_trade_date).days
    if g.trade_frequency == 'weekly' and days_diff >= 7:
        return True
    if g.trade_frequency == 'biweekly' and days_diff >= 14:
        return True
    return False

# ==================== 卖出函数 ====================
def etf_sell_trade(context):
    log.info("======================== 卖出操作开始 ========================")
    
    # 先执行固定止损（无论是否为调仓日）
    stop_loss_sell(context)
    
    # 如果不是调仓日，则跳过非止损的卖出
    if not is_trade_day(context):
        log.info("今日非调仓日，仅执行止损卖出，不进行常规调仓卖出")
        log.info("======================== 卖出操作完成 ========================")
        return

    # 获取符合条件的ETF排名（缓存到g中，供买入使用）
    g.last_ranked_etfs = get_ranked_etfs(context)
    ranked_etfs = g.last_ranked_etfs
    
    # 确定目标ETF
    target_etf = None
    if ranked_etfs and ranked_etfs[0]['score'] >= g.min_score_threshold:
        target_etf = ranked_etfs[0]['etf']
        log.debug(f"📌 选中进攻型目标ETF：{target_etf} {get_security_name(target_etf)}")
    else:
        log.info("⚠️ 无符合条件的进攻型ETF，检查防御ETF是否可用")
    
    # 检查防御ETF是否可用
    defensive_etf_available = False
    if target_etf is None:
        defensive_etf_available = check_defensive_etf_available(context)
        if defensive_etf_available:
            target_etf = g.defensive_etf
            log.info(f"📌 切换到防御ETF：{target_etf} {get_security_name(target_etf)}")
        else:
            log.info("⚠️ 防御ETF不可用，本次无目标ETF")
    
    # 构建目标ETF列表
    target_etfs = [target_etf] if target_etf else []
    target_etfs_set = set(target_etfs)
    
    # ============== 卖出不在目标列表中的持仓 ==============
    latest_positions = list(context.portfolio.positions.keys())
    for security in latest_positions:
        if (security in g.etf_pool or security == g.defensive_etf) and security not in target_etfs_set:
            position = context.portfolio.positions[security]
            if position.total_amount > 0:
                # 检查最小持仓天数（防御ETF不限制）
                if security == g.defensive_etf:
                    # 防御ETF可以随时卖出
                    pass
                else:
                    # 如果持仓未满最小天数，且不是因为止损触发，则暂不卖出
                    if security in g.holding_start_date:
                        hold_days = (context.current_dt.date() - g.holding_start_date[security]).days
                        if hold_days < g.min_holding_days:
                            log.debug(f"⏳ {security} {get_security_name(security)} 持仓仅{hold_days}天，未满最小持仓天数{g.min_holding_days}，暂不卖出")
                            continue
                # 执行卖出
                security_name = get_security_name(security)
                success = smart_order_target_value(security, 0, context)
                if success:
                    log.debug(f"📤 卖出不在目标列表的持仓: {security} {security_name}")
                    # 清除持仓开始记录
                    g.holding_start_date.pop(security, None)
                else:
                    log.warning(f"❌ 卖出失败：{security} {security_name}，非目标持仓未清仓")
    
    # 记录本次调仓日期
    g.last_trade_date = context.current_dt.date()
    log.info("======================== 卖出操作完成 ========================")

def stop_loss_sell(context):
    """独立止损函数，每日执行"""
    for security in list(context.portfolio.positions.keys()):
        if security in g.etf_pool:
            position = context.portfolio.positions[security]
            if position.total_amount > 0:
                security_name = get_security_name(security)
                current_price = position.price
                cost_price = position.avg_cost
                if cost_price > 0 and current_price <= cost_price * g.stop_loss:
                    success = smart_order_target_value(security, 0, context)
                    loss_percent = (current_price/cost_price - 1) * 100
                    if success:
                        log.info(f"🚨 固定百分比止损卖出: {security} {security_name}，亏损: {loss_percent:.2f}%")
                        # 记录止损日期
                        g.stop_loss_record[security] = context.current_dt.date()
                    else:
                        log.warning(f"❌ 固定止损失败：{security} {security_name}")

# ==================== 获取ETF排名函数 ====================
def get_ranked_etfs(context):
    """获取符合条件的ETF排名（与原逻辑相同）"""
    etf_metrics = []
    filtered_pool = g.etf_pool
    current_data = get_current_data()
    for etf in filtered_pool:
        if current_data[etf].paused:
            log.debug(f"{etf}: 今日停牌，跳过计算")
            continue
        metrics = calculate_momentum_metrics(context, etf)
        if metrics is not None:
            if 0 < metrics['score'] < g.max_score_threshold:
                etf_metrics.append(metrics)
            else: 
                log.debug(f"⚠️ {etf} 得分不满足要求！")
                
    etf_metrics.sort(key=lambda x: x['score'], reverse=True)
    return etf_metrics

# ==================== 动量指标计算函数 ====================
def calculate_momentum_metrics(context, etf):
    """计算ETF的动量指标（与原逻辑相同，但修正了冗余）"""
    try:
        lookback = g.lookback_days + 20
        prices = attribute_history(etf, lookback, '1d', ['close', 'high'])
        current_data = get_current_data()
        
        if prices.empty or len(prices) < g.lookback_days:
            log.debug(f"{etf}: 历史数据不足（仅{len(prices)}天），跳过")
            return None
        
        current_price = current_data[etf].last_price
        if current_price <= 0:
            log.debug(f"{etf}: 实时价格异常（{current_price}），跳过")
            return None
        price_series = np.append(prices["close"].values, current_price)
        
        # 过滤近3日单日大跌
        if len(price_series) >= 4:
            day1_prev = price_series[-2] if price_series[-2] > 0 else 1
            day2_prev = price_series[-3] if price_series[-3] > 0 else 1
            day3_prev = price_series[-4] if price_series[-4] > 0 else 1
            day1_ratio = price_series[-1] / day1_prev
            day2_ratio = price_series[-2] / day2_prev
            day3_ratio = price_series[-3] / day3_prev
            min_ratio = min(day1_ratio, day2_ratio, day3_ratio)
            if min_ratio < g.loss:
                log.debug(f"⚠️ {etf} 近3日单日最大跌幅{(1-min_ratio)*100:.2f}% > {(1-g.loss)*100:.2f}%阈值，直接过滤")
                return None
       
        # ========== 成交量过滤 ==========
        if g.enable_volume_check and len(price_series) > g.lookback_days:
            volume_ratio = get_volume_ratio(context, etf)
            if volume_ratio is not None:
                # 复用动量计算中的年化收益（避免重复计算）
                recent_price_series = price_series[-(g.lookback_days + 1):]
                y = np.log(recent_price_series)
                x = np.arange(len(y))
                weights = np.linspace(1, 2, len(y))
                slope, _ = np.polyfit(x, y, 1, w=weights)
                annualized_returns = math.exp(slope * 250) - 1
                if annualized_returns > g.volume_return_limit:
                    log.debug(f"{etf}: 成交量放大{volume_ratio:.2f}倍且年化收益{annualized_returns:.2f}超过阈值{g.volume_return_limit}，过滤")
                    return None

        # ========== 长期动量与R² ==========
        recent_price_series = price_series[-(g.lookback_days + 1):]
        y = np.log(recent_price_series)
        x = np.arange(len(y))
        weights = np.linspace(1, 2, len(y))
        slope, intercept = np.polyfit(x, y, 1, w=weights)
        annualized_returns = math.exp(slope * 250) - 1
        ss_res = np.sum(weights * (y - (slope * x + intercept)) ** 2)
        ss_tot = np.sum(weights * (y - np.mean(y)) ** 2)
        r_squared = 1 - ss_res / ss_tot if ss_tot else 0
        
        if g.use_r2_filter:
            if not (g.r2_min_threshold <= r_squared <= 1):
                log.debug(f"{etf}: R²={r_squared:.4f} 不在[{g.r2_min_threshold}, 1]范围内，过滤")
                return None
        
        score = annualized_returns * r_squared
        return {
            'etf': etf,
            'current_price': current_price,
            'slope': slope,
            'annualized_returns': annualized_returns,
            'r_squared': r_squared,
            'score': score,
        }
        
    except Exception as e:
        log.warning(f"计算{etf}动量指标时出错: {e}")
        return None

# ==================== 成交量过滤函数 ====================
def get_volume_ratio(context, security, lookback_days=None, threshold=None):
    """计算成交量比值，返回比值或None"""
    if lookback_days is None:
        lookback_days = g.volume_lookback
    if threshold is None:
        threshold = g.volume_threshold
    try:
        hist_data = attribute_history(security, lookback_days, '1d', ['volume'])
        if hist_data.empty or len(hist_data) < lookback_days:
            return None
        avg_volume = hist_data['volume'].mean()
        today = context.current_dt.date()
        df_vol = get_price(
            security,
            start_date=today,
            end_date=context.current_dt,
            frequency='1m',
            fields=['volume'],
            skip_paused=False,
            fq='pre',
            panel=True,
            fill_paused=False
        )
        if df_vol is None or df_vol.empty:
            return None
        current_volume = df_vol['volume'].sum()
        volume_ratio = current_volume / avg_volume if avg_volume > 0 else 0
        if volume_ratio > threshold:
            return volume_ratio
        else:
            return None
    except Exception as e:
        log.warning(f"成交量检测失败 {security}: {e}")
        return None

# ==================== 买入函数 ====================
def etf_buy_trade(context):
    log.info("======================== 买入操作开始 ========================")
    
    # 如果不是调仓日，则跳过买入（但允许补仓或空仓时买入？为了简化，非调仓日不执行任何买入）
    if not is_trade_day(context):
        log.info("今日非调仓日，跳过买入")
        log.info("======================== 买入操作完成 ========================")
        return
    
    # 获取排名（优先使用缓存）
    if g.last_ranked_etfs is not None:
        ranked_etfs = g.last_ranked_etfs
        log.debug("使用缓存排名数据")
    else:
        ranked_etfs = get_ranked_etfs(context)
        g.last_ranked_etfs = ranked_etfs

    # 记录排名前五
    if ranked_etfs:
        log.info("========================== 排名前五 ==========================")
        for idx, metrics in enumerate(ranked_etfs[:5], start=1):  
            etf_name = get_security_name(metrics['etf'])
            annualized_pct = metrics['annualized_returns'] * 100
            log.info(f"{idx}. {metrics['etf']} {etf_name}: 动量得分={metrics['score']:.4f}|年化收益={annualized_pct:.2f}%|R²={metrics['r_squared']:.4f}")
    
    # 确定目标ETF
    target_etf = None
    target_score = 0
    if ranked_etfs and ranked_etfs[0]['score'] >= g.min_score_threshold:
        target_etf = ranked_etfs[0]['etf']
        target_score = ranked_etfs[0]['score']
        top_metrics = ranked_etfs[0]
        etf_name = get_security_name(target_etf)
        log.debug(f"🎯 候选目标: {target_etf} {etf_name}，得分: {target_score:.4f}")
    else:
        if check_defensive_etf_available(context):
            target_etf = g.defensive_etf
            target_score = 0  # 防御ETF得分视为0
            etf_name = get_security_name(target_etf)
            log.info(f"🛡️ 候选目标: 防御ETF {target_etf} {etf_name}")
        else:
            log.info("💤 无进攻型ETF且防御ETF不可用，保持空仓")
    
    # 无目标则直接返回
    if target_etf is None:
        return
    
    # 检查止损冷却
    if target_etf in g.stop_loss_record:
        last_stop_date = g.stop_loss_record[target_etf]
        days_since_stop = (context.current_dt.date() - last_stop_date).days
        if days_since_stop <= g.stop_loss_cooldown:
            log.info(f"⏳ {target_etf} {get_security_name(target_etf)} 在{g.stop_loss_cooldown}天内刚止损过，冷却期内不买入")
            # 如果目标被冷却，则考虑次优目标？这里简单跳过，可扩展为选取次优
            return
    
    # 获取当前持仓（只考虑ETF池和防御ETF）
    current_positions = []
    for sec in context.portfolio.positions:
        if sec in g.etf_pool or sec == g.defensive_etf:
            pos = context.portfolio.positions[sec]
            if pos.total_amount > 0:
                current_positions.append(sec)
    
    # 如果当前已有持仓，需要判断是否换仓
    if current_positions:
        current_sec = current_positions[0]  # 由于只持有一个，取第一个
        # 如果当前持仓就是目标ETF，则无需换仓（直接调整仓位）
        if current_sec == target_etf:
            log.debug(f"当前持仓已是目标ETF，直接调整仓位")
        else:
            # 获取当前持仓的得分（如果是防御ETF则得分为0）
            current_score = 0
            if current_sec in g.etf_pool:
                # 查找排名中该ETF的得分（若不在排名中则得分为0）
                for m in ranked_etfs:
                    if m['etf'] == current_sec:
                        current_score = m['score']
                        break
            # 应用换仓阈值：新得分必须大于当前得分 * (1 + threshold)
            if target_score <= current_score * (1 + g.score_diff_threshold):
                log.info(f"⏳ 换仓阈值未触发: 当前得分{current_score:.4f}，目标得分{target_score:.4f}，阈值{g.score_diff_threshold*100}%，暂不换仓")
                # 仍调整现有持仓的仓位（可能之前部分卖出导致仓位不足）
                # 但为了简化，此处不调整，保持原有持仓。若想调整可取消注释下面调整代码
                # 但注意：若当前持仓不是目标，但阈值未过，我们不应买入新标的，也不应加仓旧标的（因为旧的可能得分低）
                # 所以直接返回，保持现状
                return
            else:
                log.info(f"✅ 换仓阈值触发: 当前得分{current_score:.4f} -> 目标得分{target_score:.4f}，进行换仓")
                # 将在后续卖出旧标的后买入新标的（卖出已在etf_sell_trade中执行）
                # 但这里需要确保旧标的已被卖出，否则不能买入
                # 检查旧标的是否还在持仓
                if current_sec in context.portfolio.positions and context.portfolio.positions[current_sec].total_amount > 0:
                    log.info(f"⚠️ 旧持仓 {current_sec} 尚未卖出，等待卖出完成")
                    return
    else:
        # 空仓，直接买入目标
        log.debug("当前空仓，准备买入目标")
    
    # 执行买入
    total_value = context.portfolio.total_value
    target_value = total_value
    
    # 获取当前目标持仓市值
    current_value = 0
    if target_etf in context.portfolio.positions:
        position = context.portfolio.positions[target_etf]
        if position.total_amount > 0:
            current_value = position.total_amount * position.price
    
    if abs(current_value - target_value) > target_value * 0.05 or current_value == 0:
        success = smart_order_target_value(target_etf, target_value, context)
        if success:
            etf_name = get_security_name(target_etf)
            action = "买入" if current_value < target_value else "调仓"
            log.debug(f"📦 {action}: {target_etf} {etf_name}，目标金额: {target_value:.2f}")
            # 记录持仓开始日期（如果是新买入）
            if current_value == 0:
                g.holding_start_date[target_etf] = context.current_dt.date()
    
    log.info("======================== 买入操作完成 ========================")

# ==================== 辅助函数 ====================
def get_security_name(security):
    current_data = get_current_data()
    return current_data[security].name

def check_defensive_etf_available(context):
    current_data = get_current_data()
    defensive_etf = g.defensive_etf
    if current_data[defensive_etf].paused:
        log.info(f"防御性ETF {defensive_etf} 今日停牌")
        return False
    if current_data[defensive_etf].last_price >= current_data[defensive_etf].high_limit:
        log.info(f"防御性ETF {defensive_etf} 当前涨停")
        return False
    if current_data[defensive_etf].last_price <= current_data[defensive_etf].low_limit:
        log.info(f"防御性ETF {defensive_etf} 当前跌停")
        return False
    return True

def smart_order_target_value(security, target_value, context):
    current_data = get_current_data()
    if current_data[security].paused:
        log.info(f"{security} {get_security_name(security)}: 今日停牌，跳过交易")
        return False
    if current_data[security].last_price >= current_data[security].high_limit:
        log.info(f"{security} {get_security_name(security)}: 当前涨停，跳过买入")
        return False
    if current_data[security].last_price <= current_data[security].low_limit:
        log.info(f"{security} {get_security_name(security)}: 当前跌停，跳过卖出")
        return False
    current_price = current_data[security].last_price
    if current_price == 0:
        log.info(f"{security} {get_security_name(security)}: 当前价格为0，跳过交易")
        return False
    target_amount = int(target_value / current_price)
    target_amount = (target_amount // 100) * 100
    if target_amount <= 0 and target_value > 0:
        target_amount = 100
    current_position = context.portfolio.positions.get(security, None)
    current_amount = current_position.total_amount if current_position else 0
    amount_diff = target_amount - current_amount
    trade_value = abs(amount_diff) * current_price
    if 0 < trade_value < g.min_money:
        log.info(f"{security} {get_security_name(security)}: 交易金额{trade_value:.2f}小于最小交易额{g.min_money}，跳过交易")
        return False
    if amount_diff < 0:
        closeable_amount = current_position.closeable_amount if current_position else 0
        if closeable_amount == 0:
            log.info(f"{security} {get_security_name(security)}: 当天买入不可卖出(T+1)")
            return False
        amount_diff = -min(abs(amount_diff), closeable_amount)
    if amount_diff != 0:
        order_result = order(security, amount_diff)
        if order_result:
            g.positions[security] = target_amount
            security_name = get_security_name(security)
            if amount_diff > 0:
                log.info(f"📥 买入 {security} {security_name}，数量: {amount_diff}，价格: {current_price:.3f}")
            else:
                log.info(f"📤 卖出 {security} {security_name}，数量: {abs(amount_diff)}，价格: {current_price:.3f}")
            return True
        else:
            log.warning(f"下单失败: {security} {get_security_name(security)}，数量: {amount_diff}")
            return False
    return False

def trade(context):
    pass

