#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
EmuTrader 滑点功能JoinQuant兼容性验证

验证滑点功能100%兼容JoinQuant API规范。
"""

import sys
import os

# 添加项目路径
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))

from emutrader import (
    get_jq_account, FixedSlippage, PriceRelatedSlippage, StepRelatedSlippage,
    set_slippage, order_shares, order_value, order_target_percent
)


def test_jq_compatibility():
    """测试JoinQuant API兼容性"""
    print("=== JoinQuant API兼容性验证 ===\n")
    
    # 1. 创建策略上下文（与JQ完全相同）
    print("1. 创建策略上下文...")
    context = get_jq_account("jq_compatibility_test", 100000)
    print(f"初始资金: {context.portfolio.available_cash:.2f}")
    print("[OK] get_jq_account() 函数正常工作")
    
    # 2. 测试set_slippage API（JQ规范）
    print("\n2. 测试set_slippage API...")
    
    # 2.1 全局固定滑点
    set_slippage(FixedSlippage(0.02))
    print("[OK] set_slippage(FixedSlippage(0.02)) - 全局固定滑点")
    
    # 2.2 按类型设置百分比滑点
    set_slippage(PriceRelatedSlippage(0.002), type='stock')
    print("[OK] set_slippage(PriceRelatedSlippage(0.002), type='stock') - 股票百分比滑点")
    
    # 2.3 按具体标的设置固定滑点
    set_slippage(FixedSlippage(0.05), type='stock', ref='000001.SZ')
    print("[OK] set_slippage(FixedSlippage(0.05), type='stock', ref='000001.SZ') - 特定股票滑点")
    
    # 2.4 期货跳数滑点
    set_slippage(StepRelatedSlippage(2), type='futures', ref='IF')
    print("[OK] set_slippage(StepRelatedSlippage(2), type='futures', ref='IF') - 期货跳数滑点")
    
    # 3. 验证滑点配置优先级（JQ规范）
    print("\n3. 验证滑点配置优先级...")
    
    from emutrader.api import get_current_emutrader
    emu = get_current_emutrader()
    
    # 获取不同证券的滑点配置
    specific_slippage = emu.get_slippage_info('000001.SZ', 'stock')
    stock_slippage = emu.get_slippage_info('000002.SZ', 'stock')
    futures_slippage = emu.get_slippage_info('IF2312', 'futures')
    
    print(f"000001.SZ滑点: {specific_slippage['type']} - {specific_slippage['params']}")
    print(f"000002.SZ滑点: {stock_slippage['type']} - {stock_slippage['params']}")
    print(f"IF2312滑点: {futures_slippage['type']} - {futures_slippage['params']}")
    
    # 验证优先级：具体标的 > 类型 > 全局
    assert specific_slippage['type'] == 'FixedSlippage'  # 特定配置
    assert stock_slippage['type'] == 'PriceRelatedSlippage'  # 类型配置
    assert futures_slippage['type'] == 'StepRelatedSlippage'  # 类型配置
    print("✓ 滑点优先级正确：具体标的 > 类型 > 全局")
    
    # 4. 测试货币基金零滑点（JQ规范）
    print("\n4. 测试货币基金零滑点...")
    
    # 即使设置滑点，货币基金也应该强制为0
    set_slippage(FixedSlippage(0.1), type='mmf')
    mmf_slippage = emu.get_slippage_info('511880.SH', 'mmf')
    
    assert mmf_slippage['type'] == 'FixedSlippage'
    assert mmf_slippage['params']['fixed_value'] == 0.0
    print("✓ 货币基金滑点强制为0")
    
    # 5. 测试交易中的滑点应用（JQ行为）
    print("\n5. 测试交易中的滑点应用...")
    
    # 更新价格
    context.update_market_price('000001.SZ', 20.0)
    context.update_market_price('000002.SZ', 30.0)
    
    # 记录初始资金
    initial_cash = context.portfolio.available_cash
    
    # 使用不同下单函数（全部兼容JQ）
    print("\n5.1 测试order_shares...")
    order1 = order_shares('000001.SZ', 1000, 20.0)  # 指定价格
    if order1:
        position1 = context.portfolio.get_position('000001.SZ')
        print(f"order_shares成交价: {position1.avg_cost:.4f} (预期20.025)")
        # 验证滑点生效：应该高于预期价格
        assert position1.avg_cost > 20.0
    
    print("\n5.2 测试order_value...")
    order2 = order_value('000002.SZ', 30000)  # 按金额下单
    if order2:
        position2 = context.portfolio.get_position('000002.SZ')
        print(f"order_value成交数量: {position2.total_amount}")
        print(f"order_value成交价: {position2.avg_cost:.4f} (预期30.03)")
        # 验证滑点生效
        assert position2.avg_cost > 30.0
    
    print("\n5.3 测试order_target_percent...")
    order3 = order_target_percent('000001.SZ', 0.1)  # 调整到10%仓位
    if order3:
        position1_updated = context.portfolio.get_position('000001.SZ')
        print(f"order_target_percent后持仓: {position1_updated.total_amount}")
    
    print("✓ 所有JQ下单函数都正确应用滑点")
    
    # 6. 验证账户属性访问（JQ兼容）
    print("\n6. 验证账户属性访问...")
    
    # 验证context.portfolio正常访问
    total_value = context.portfolio.total_value
    available_cash = context.portfolio.available_cash
    market_value = context.portfolio.market_value
    
    print(f"总资产: {total_value:.2f}")
    print(f"可用资金: {available_cash:.2f}")
    print(f"持仓市值: {market_value:.2f}")
    print("✓ context.portfolio属性正常访问")
    
    # 验证持仓信息
    position = context.portfolio.get_position('000001.SZ')
    if position:
        print(f"持仓数量: {position.total_amount}")
        print(f"平均成本: {position.avg_cost:.4f}")
        print(f"持仓盈亏: {position.pnl:.2f}")
        print("✓ 持仓信息包含滑点影响")
    
    print("\n=== JoinQuant API兼容性验证通过 ===")


def test_api_specification_compliance():
    """测试API规范符合性"""
    print("\n=== API规范符合性验证 ===\n")
    
    # 1. 验证支持的交易品种类型（JQ规范）
    print("1. 验证支持的交易品种类型...")
    
    supported_types = [
        'stock', 'fund', 'mmf', 'fja', 'fjb', 'fjm',
        'index_futures', 'futures', 'bond_fund', 'stock_fund',
        'QDII_fund', 'mixture_fund', 'money_market_fund'
    ]
    
    context = get_jq_account("api_spec_test", 100000)
    
    for sec_type in supported_types:
        try:
            set_slippage(FixedSlippage(0.01), type=sec_type)
            print(f"✓ {sec_type} - 支持的交易品种类型")
        except Exception as e:
            print(f"❌ {sec_type} - 不支持: {e}")
    
    # 2. 验证默认滑点（JQ规范）
    print("\n2. 验证默认滑点...")
    
    from emutrader.api import get_current_emutrader
    emu = get_current_emutrader()
    
    # 清除所有设置，恢复默认
    emu.clear_slippage()
    
    default_info = emu.get_slippage_info('000001.SZ', 'stock')
    assert default_info['type'] == 'PriceRelatedSlippage'
    assert abs(default_info['params']['percentage'] - 0.00246) < 1e-6
    print(f"✓ 默认滑点: PriceRelatedSlippage(0.00246)")
    
    # 3. 验证滑点计算规则（JQ规范）
    print("\n3. 验证滑点计算规则...")
    
    # 设置固定滑点0.2
    set_slippage(FixedSlippage(0.2))
    
    # 买入：预期价 + 滑点/2
    buy_price = emu.calculate_slippage_price('TEST', 100.0, 100, 'open', 'stock')
    expected_buy = 100.0 + 0.2 / 2  # 100.1
    assert abs(buy_price - expected_buy) < 1e-6
    
    # 卖出：预期价 - 滑点/2
    sell_price = emu.calculate_slippage_price('TEST', 100.0, 100, 'close', 'stock')
    expected_sell = 100.0 - 0.2 / 2  # 99.9
    assert abs(sell_price - expected_sell) < 1e-6
    
    print(f"✓ 买入价格: {buy_price} (预期: {expected_buy})")
    print(f"✓ 卖出价格: {sell_price} (预期: {expected_sell})")
    
    # 4. 验证价格保护机制（JQ规范）
    print("\n4. 验证价格保护机制...")
    
    # 测试最低价格限制
    low_sell_price = emu.calculate_slippage_price('TEST', 0.01, 100, 'close', 'stock')
    assert low_sell_price == 0.01  # 不应该低于0.01
    print(f"✓ 最低价格保护: {low_sell_price}")
    
    print("\n=== API规范符合性验证通过 ===")


def test_error_handling():
    """测试错误处理"""
    print("\n=== 错误处理验证 ===\n")
    
    # 1. 测试无效滑点对象
    print("1. 测试无效滑点对象...")
    
    try:
        set_slippage("invalid_slippage")
        print("❌ 应该抛出异常")
    except Exception as e:
        print(f"✓ 正确捕获无效滑点对象: {type(e).__name__}")
    
    # 2. 测试无效交易品种类型
    print("\n2. 测试无效交易品种类型...")
    
    try:
        set_slippage(FixedSlippage(0.01), type='invalid_type')
        print("❌ 应该抛出异常")
    except Exception as e:
        print(f"✓ 正确捕获无效类型: {type(e).__name__}")
    
    # 3. 测试参数逻辑错误
    print("\n3. 测试参数逻辑错误...")
    
    try:
        # 设置具体标的但未指定type
        set_slippage(FixedSlippage(0.01), ref='000001.SZ')
        print("❌ 应该抛出异常")
    except Exception as e:
        print(f"✓ 正确捕获参数逻辑错误: {type(e).__name__}")
    
    # 4. 测试负值滑点
    print("\n4. 测试负值滑点...")
    
    try:
        set_slippage(FixedSlippage(-0.01))
        print("❌ 应该抛出异常")
    except Exception as e:
        print(f"✓ 正确捕获负值滑点: {type(e).__name__}")
    
    print("\n=== 错误处理验证通过 ===")


def main():
    """主函数"""
    print("EmuTrader 滑点功能 - JoinQuant兼容性验证")
    print("=" * 60)
    
    # 运行所有验证
    test_jq_compatibility()
    test_api_specification_compliance()
    test_error_handling()
    
    print("\n" + "=" * 60)
    print("🎉 所有验证通过！滑点功能100%兼容JoinQuant API")
    print("\n功能特性:")
    print("✓ 支持FixedSlippage、PriceRelatedSlippage、StepRelatedSlippage")
    print("✓ 支持全局、类型、具体标的三级配置")
    print("✓ 货币基金强制零滑点")
    print("✓ 正确的滑点优先级和计算规则")
    print("✓ 完整的错误处理和参数验证")
    print("✓ 100%兼容JoinQuant API规范")


if __name__ == "__main__":
    main()