Skill: daidaotian/joinquant-strategy (version 1.0.0)

# joinquant-strategy Skill 帮助在Cursor中编写聚宽(JoinQuant)量化策略的工具集。 ## 主要功能 - 提供聚宽策略的标准模板 - 包含常用代码片段 - 提供API快速参考 - 集成最佳实践示例 ## 目录结构 ``` joinquant-strategy/ ├── README.md # Skill 说明文档 ├── templates/ # 策略模板目录 │ ├── basic_template.py # 基础策略模板 │ ├── dual_strategy_template.py # 双策略模板 │ └── etf_rotation_template.py # ETF轮动策略模板 ├── snippets/ # 代码片段目录 │ ├── data_fetching.md # 数据获取常用代码 │ ├── order_handling.md # 下单处理代码 │ ├── risk_control.md # 风险控制代码 │ └── technical_analysis.md # 技术分析代码 ├── api_reference/ # API参考文档 │ ├── core_functions.md # 核心函数说明 │ ├── data_functions.md # 数据获取函数说明 │ └── order_functions.md # 下单函数说明 └── examples/ # 示例策略 └── momentum_etf_strategy.py # 动量ETF策略 ``` ## 功能说明 ### 策略模板 - **basic_template.py**: 基础策略模板，包含完整的策略结构和基本逻辑 - **dual_strategy_template.py**: 双策略模板，基于示例实现的双策略框架 - **etf_rotation_template.py**: ETF轮动策略模板，用于实现ETF轮动交易策略 ### 代码片段 - **data_fetching.md**: 数据获取常用代码，包括历史数据、财务数据等获取方法 - **order_handling.md**: 下单处理代码，包括委托下单、撤单等操作 - **risk_control.md**: 风险控制代码，包括止损、止盈、仓位管理等 - **technical_analysis.md**: 技术分析代码，包括常用技术指标计算 ### API参考文档 - **core_functions.md**: 核心函数说明，包括策略初始化、运行等核心功能 - **data_functions.md**: 数据获取函数说明，详细介绍数据API的使用方法 - **order_functions.md**: 下单函数说明，详细介绍下单API的使用方法 ### 示例策略 - **momentum_etf_strategy.py**: 动量ETF策略，基于示例整理的完整策略实现 ## 使用方法 在Cursor中使用此skill时，可以： 1. **快速开始**：使用`/template`命令选择策略模板 2. **代码补全**：输入`/snippet`查看常用代码片段 3. **API查询**：输入`/api 函数名`快速查看API用法 4. **最佳实践**：参考`examples/`目录学习复杂策略的编写 ## 注意事项 - 本工具包提供的代码和模板仅供参考，实际使用时需要根据具体情况进行调整 - 策略开发过程中，应注意风险管理和回测验证 - 请遵守 JoinQuant 平台的相关规定和限制 ## 版本信息 - 版本: 1.0.0 - 最后更新: 2026-03-10

path: api_reference/core_functions.md
content: # 聚宽核心API参考

## 初始化函数
- `initialize(context)` - 策略初始化，只执行一次
- `process_initialize(context)` - 进程重启时执行
- `after_code_changed(context)` - 代码修改后执行

## 定时运行函数
- `run_daily(func, time, reference_security)` - 每天运行
- `run_weekly(func, weekday, time, reference_security, force)` - 每周运行
- `run_monthly(func, monthday, time, reference_security, force)` - 每月运行

## 数据获取函数
- `attribute_history(security, count, unit, fields, skip_paused, fq)` - 单只股票历史数据
- `history(count, unit, field, security_list, skip_paused, fq)` - 多只股票历史数据
- `get_current_data()` - 获取当前数据
- `get_fundamentals(query_object, date, statDate)` - 获取财务数据

## 下单函数
- `order(security, amount)` - 按股数下单
- `order_value(security, value)` - 按金额下单
- `order_target(security, amount)` - 调整至目标股数
- `order_target_value(security, value)` - 调整至目标金额

## 其他常用函数
- `set_benchmark(security)` - 设置基准
- `set_option(name, value)` - 设置选项
- `set_order_cost(cost, type)` - 设置手续费
- `set_slippage(slippage)` - 设置滑点
- `get_all_securities(types, date)` - 获取所有证券
- `get_index_stocks(index_symbol, date)` - 获取指数成分股
- `get_industry_stocks(industry_code, date)` - 获取行业成分股
- `get_concept_stocks(concept_code, date)` - 获取概念成分股
- `log.info(msg)` - 输出信息日志
- `log.warning(msg)` - 输出警告日志
- `log.error(msg)` - 输出错误日志
- `record(**kwargs)` - 记录变量用于图表显示


path: api_reference/data_functions.md
content: # 数据获取函数说明

## 1. 历史行情数据

### 1.1 get_price 函数

```python
def get_price(
    security,  # 股票代码，单个代码或代码列表
    start_date=None,  # 开始日期
    end_date=None,  # 结束日期
    frequency='daily',  # 数据频率：'daily', '1m', '5m', '15m', '30m', '60m'
    fields=None,  # 返回字段，默认为 ['open', 'close', 'high', 'low', 'volume', 'money']
    skip_paused=False,  # 是否跳过停牌日期
    fq='pre'  # 复权方式：'pre' 前复权, 'post' 后复权, None 不复权
):
    """获取历史行情数据"""
    pass
```

**参数说明**：
- `security`：股票代码，可以是单个代码或代码列表
- `start_date`：开始日期，格式为 'YYYY-MM-DD'
- `end_date`：结束日期，格式为 'YYYY-MM-DD'
- `frequency`：数据频率，可选 'daily', '1m', '5m', '15m', '30m', '60m'
- `fields`：返回字段，默认为 ['open', 'close', 'high', 'low', 'volume', 'money']
- `skip_paused`：是否跳过停牌日期，默认为 False
- `fq`：复权方式，可选 'pre' 前复权, 'post' 后复权, None 不复权

**返回值**：
- DataFrame，包含指定股票的历史行情数据

**示例**：
```python
# 获取单只股票的日线数据
df = get_price('600000.XSHG', start_date='2020-01-01', end_date='2020-12-31')

# 获取多只股票的分钟数据
df = get_price(['600000.XSHG', '600519.XSHG'], start_date='2020-01-01', end_date='2020-01-02', frequency='1m')
```

## 2. 财务数据

### 2.1 get_fundamentals 函数

```python
def get_fundamentals(
    query_object,  # 查询对象
    date=None,  # 查询日期
    statDate=None  # 财报统计日期
):
    """获取财务数据"""
    pass
```

**参数说明**：
- `query_object`：查询对象，使用 query() 函数构建
- `date`：查询日期，格式为 'YYYY-MM-DD'
- `statDate`：财报统计日期，格式为 'YYYY-MM'

**返回值**：
- DataFrame，包含查询的财务数据

**示例**：
```python
# 查询市盈率和市净率
df = get_fundamentals(
    query(valuation.code, valuation.pe_ratio, valuation.pb_ratio),
    date='2020-12-31'
)

# 带筛选条件的查询
df = get_fundamentals(
    query(valuation.code, valuation.pe_ratio)
    .filter(valuation.pe_ratio < 20)
    .order_by(valuation.pe_ratio.asc())
    .limit(10),
    date='2020-12-31'
)
```

### 2.2 query 函数

```python
query(
    indicator.roe,  # 要查询的字段
    valuation.pe_ratio
).filter(
    indicator.roe > 0.1  # 筛选条件
).order_by(
    indicator.roe.desc()  # 排序方式
).limit(
    10  # 限制返回数量
)
```

**功能**：
- 构建财务数据查询对象

## 3. 股票列表

### 3.1 get_all_securities 函数

```python
def get_all_securities(
    types=['stock'],  # 证券类型，可选 'stock', 'fund', 'index', 'futures', 'option'
    date=None  # 查询日期
):
    """获取所有证券列表"""
    pass
```

**参数说明**：
- `types`：证券类型列表
- `date`：查询日期，格式为 'YYYY-MM-DD'

**返回值**：
- DataFrame，包含证券列表及基本信息

**示例**：
```python
# 获取所有股票列表
stock_list = get_all_securities(['stock'], date='2020-01-01')

# 获取所有基金列表
fund_list = get_all_securities(['fund'], date='2020-01-01')
```

### 3.2 get_index_stocks 函数

```python
def get_index_stocks(
    index_symbol,  # 指数代码
    date=None  # 查询日期
):
    """获取指数成分股"""
    pass
```

**参数说明**：
- `index_symbol`：指数代码
- `date`：查询日期，格式为 'YYYY-MM-DD'

**返回值**：
- 列表，包含指数成分股代码

**示例**：
```python
# 获取沪深300成分股
hs300 = get_index_stocks('000300.XSHG', date='2020-01-01')

# 获取中证500成分股
zz500 = get_index_stocks('000905.XSHG', date='2020-01-01')
```

### 3.3 get_industry_stocks 函数

```python
def get_industry_stocks(
    industry_code,  # 行业代码
    date=None  # 查询日期
):
    """获取行业成分股"""
    pass
```

**参数说明**：
- `industry_code`：行业代码
- `date`：查询日期，格式为 'YYYY-MM-DD'

**返回值**：
- 列表，包含行业成分股代码

**示例**：
```python
# 获取农林牧渔行业成分股
agriculture_stocks = get_industry_stocks('801010', date='2020-01-01')
```

### 3.4 get_concept_stocks 函数

```python
def get_concept_stocks(
    concept_code,  # 概念代码
    date=None  # 查询日期
):
    """获取概念成分股"""
    pass
```

**参数说明**：
- `concept_code`：概念代码
- `date`：查询日期，格式为 'YYYY-MM-DD'

**返回值**：
- 列表，包含概念成分股代码

**示例**：
```python
# 获取融资融券概念成分股
margin_stocks = get_concept_stocks('GN001', date='2020-01-01')
```

## 4. 实时数据

### 4.1 get_current_data 函数

```python
def get_current_data():
    """获取实时数据"""
    pass
```

**返回值**：
- 字典，包含所有股票的实时数据

**示例**：
```python
# 获取实时数据
current_data = get_current_data()

# 获取单只股票的实时价格
price = current_data['600000.XSHG'].last_price

# 获取股票是否停牌
is_paused = current_data['600000.XSHG'].paused

# 获取股票是否涨停
is_limit_up = current_data['600000.XSHG'].limit_up

# 获取股票是否跌停
is_limit_down = current_data['600000.XSHG'].limit_down
```

## 5. 宏观经济数据

### 5.1 macro 模块

```python
# 获取GDP数据
df = macro.MAC_INDUSTRY_GDP.get_data()

# 获取利率数据
df = macro.MAC_INTEREST_RATE.get_data()

# 获取汇率数据
df = macro.MAC_EXCHANGE_RATE.get_data()

# 获取CPI数据
df = macro.MAC_CPI.get_data()

# 获取PPI数据
df = macro.MAC_PPI.get_data()
```

**功能**：
- 获取宏观经济指标数据

## 6. 其他数据函数

### 6.1 get_dividend 函数

```python
def get_dividend(
    security,  # 股票代码
    start_date=None,  # 开始日期
    end_date=None  # 结束日期
):
    """获取分红数据"""
    pass
```

**参数说明**：
- `security`：股票代码
- `start_date`：开始日期，格式为 'YYYY-MM-DD'
- `end_date`：结束日期，格式为 'YYYY-MM-DD'

**返回值**：
- DataFrame，包含分红数据

**示例**：
```python
# 获取分红数据
df = get_dividend('600000.XSHG', start_date='2010-01-01', end_date='2020-12-31')
```

### 6.2 get_split 函数

```python
def get_split(
    security,  # 股票代码
    start_date=None,  # 开始日期
    end_date=None  # 结束日期
):
    """获取除权除息数据"""
    pass
```

**参数说明**：
- `security`：股票代码
- `start_date`：开始日期，格式为 'YYYY-MM-DD'
- `end_date`：结束日期，格式为 'YYYY-MM-DD'

**返回值**：
- DataFrame，包含除权除息数据

**示例**：
```python
# 获取除权除息数据
df = get_split('600000.XSHG', start_date='2010-01-01', end_date='2020-12-31')
```

### 6.3 get_money_flow 函数

```python
def get_money_flow(
    security_list,  # 股票代码列表
    start_date=None,  # 开始日期
    end_date=None  # 结束日期
):
    """获取资金流向数据"""
    pass
```

**参数说明**：
- `security_list`：股票代码列表
- `start_date`：开始日期，格式为 'YYYY-MM-DD'
- `end_date`：结束日期，格式为 'YYYY-MM-DD'

**返回值**：
- DataFrame，包含资金流向数据

**示例**：
```python
# 获取资金流向数据
df = get_money_flow(['600000.XSHG', '600519.XSHG'], start_date='2020-01-01', end_date='2020-01-31')
```


path: api_reference/order_functions.md
content: # 下单函数说明

## 1. 基本下单函数

### 1.1 order 函数

```python
def order(
    security,  # 股票代码
    amount,  # 交易数量，正数为买入，负数为卖出
    style=None  # 下单方式
):
    """下单函数"""
    pass
```

**参数说明**：
- `security`：股票代码
- `amount`：交易数量，正数为买入，负数为卖出
- `style`：下单方式，默认为市价单

**返回值**：
- Order对象，包含订单信息

**示例**：
```python
# 买入100股
order('600000.XSHG', 100)

# 卖出100股
order('600000.XSHG', -100)

# 限价买入
order('600000.XSHG', 100, style=LimitOrder(price=10.5))
```

### 1.2 order_target 函数

```python
def order_target(
    security,  # 股票代码
    amount,  # 目标持仓数量
    style=None  # 下单方式
):
    """调整持仓到目标数量"""
    pass
```

**参数说明**：
- `security`：股票代码
- `amount`：目标持仓数量
- `style`：下单方式，默认为市价单

**返回值**：
- Order对象，包含订单信息

**示例**：
```python
# 调整持仓到1000股
order_target('600000.XSHG', 1000)

# 清仓
order_target('600000.XSHG', 0)
```

### 1.3 order_value 函数

```python
def order_value(
    security,  # 股票代码
    value,  # 交易金额，正数为买入，负数为卖出
    style=None  # 下单方式
):
    """按金额下单"""
    pass
```

**参数说明**：
- `security`：股票代码
- `value`：交易金额，正数为买入，负数为卖出
- `style`：下单方式，默认为市价单

**返回值**：
- Order对象，包含订单信息

**示例**：
```python
# 买入1万元
order_value('600000.XSHG', 10000)

# 卖出1万元
order_value('600000.XSHG', -10000)
```

### 1.4 order_target_value 函数

```python
def order_target_value(
    security,  # 股票代码
    value,  # 目标持仓金额
    style=None  # 下单方式
):
    """调整持仓到目标金额"""
    pass
```

**参数说明**：
- `security`：股票代码
- `value`：目标持仓金额
- `style`：下单方式，默认为市价单

**返回值**：
- Order对象，包含订单信息

**示例**：
```python
# 调整持仓到5万元
order_target_value('600000.XSHG', 50000)

# 清仓
order_target_value('600000.XSHG', 0)
```

## 2. 下单方式

### 2.1 LimitOrder 限价单

```python
order('600000.XSHG', 100, style=LimitOrder(price=10.5))
```

**参数说明**：
- `price`：限价价格

**功能**：
- 以指定价格限价买入或卖出

### 2.2 MarketOrder 市价单

```python
order('600000.XSHG', 100, style=MarketOrder())
```

**功能**：
- 以市价买入或卖出

### 2.3 StopOrder 止损单

```python
order('600000.XSHG', -100, style=StopOrder(price=9.5))
```

**参数说明**：
- `price`：止损价格

**功能**：
- 当价格达到指定止损价格时执行卖出

### 2.4 StopLimitOrder 止损限价单

```python
order('600000.XSHG', -100, style=StopLimitOrder(stop_price=9.5, limit_price=9.4))
```

**参数说明**：
- `stop_price`：止损价格
- `limit_price`：限价价格

**功能**：
- 当价格达到指定止损价格时，以限价单执行卖出

## 3. 订单操作

### 3.1 cancel_order 函数

```python
def cancel_order(order_id):
    """撤单"""
    pass
```

**参数说明**：
- `order_id`：订单ID

**返回值**：
- bool，撤单是否成功

**示例**：
```python
# 下单
order_result = order('600000.XSHG', 100)
if order_result:
    # 撤单
    cancel_order(order_result.order_id)
```

### 3.2 get_orders 函数

```python
def get_orders(
    security=None,  # 股票代码
    status=None,  # 订单状态
    start_date=None,  # 开始日期
    end_date=None  # 结束日期
):
    """获取订单列表"""
    pass
```

**参数说明**：
- `security`：股票代码
- `status`：订单状态，可选 'submitted', 'filled', 'cancelled', 'rejected'
- `start_date`：开始日期，格式为 'YYYY-MM-DD'
- `end_date`：结束日期，格式为 'YYYY-MM-DD'

**返回值**：
- 订单对象列表

**示例**：
```python
# 获取所有订单
orders = get_orders()

# 获取已成交订单
filled_orders = get_orders(status='filled')
```

### 3.3 get_order 函数

```python
def get_order(order_id):
    """获取单个订单"""
    pass
```

**参数说明**：
- `order_id`：订单ID

**返回值**：
- Order对象，包含订单详细信息

**示例**：
```python
# 下单
order_result = order('600000.XSHG', 100)
if order_result:
    # 获取订单信息
    order_info = get_order(order_result.order_id)
    log.info(f'订单状态：{order_info.status}')
```

## 4. 订单对象属性

### 4.1 Order 对象属性

```python
# 订单ID
order.order_id

# 股票代码
order.security

# 交易方向（1为买入，-1为卖出）
order.direction

# 交易数量
order.amount

# 交易价格
order.price

# 交易金额
order.value

# 订单状态
order.status  # 'submitted', 'filled', 'cancelled', 'rejected'

# 下单时间
order.create_time

# 成交时间
order.fill_time
```

**属性说明**：
- `order_id`：订单ID
- `security`：股票代码
- `direction`：交易方向，1为买入，-1为卖出
- `amount`：交易数量
- `price`：交易价格
- `value`：交易金额
- `status`：订单状态，可选 'submitted', 'filled', 'cancelled', 'rejected'
- `create_time`：下单时间
- `fill_time`：成交时间

## 5. 批量下单

### 5.1 批量买入

```python
def batch_buy(stock_list, cash_per_stock):
    """批量买入"""
    for stock in stock_list:
        order_value(stock, cash_per_stock)

# 使用示例
stock_list = ['600000.XSHG', '600519.XSHG', '000001.XSHE']
cash_per_stock = context.portfolio.available_cash / len(stock_list)
batch_buy(stock_list, cash_per_stock)
```

### 5.2 批量卖出

```python
def batch_sell(stock_list):
    """批量卖出"""
    for stock in stock_list:
        order_target(stock, 0)

# 使用示例
batch_sell(list(context.portfolio.positions.keys()))
```

## 6. 下单策略

### 6.1 等权配置

```python
def equal_weight_allocation(stock_list, total_value):
    """等权配置"""
    target_weight = 1.0 / len(stock_list)
    for stock in stock_list:
        target_value = total_value * target_weight
        order_target_value(stock, target_value)

# 使用示例
equal_weight_allocation(['600000.XSHG', '600519.XSHG'], context.portfolio.total_value)
```

### 6.2 按市值加权

```python
def market_cap_weighted_allocation(stock_list, total_value):
    """按市值加权配置"""
    # 获取市值数据
    df = get_fundamentals(
        query(valuation.code, valuation.market_cap)
        .filter(valuation.code.in_(stock_list)),
        date=context.current_dt.strftime('%Y-%m-%d')
    )
    
    if not df.empty:
        # 计算总市值
        total_market_cap = df['market_cap'].sum()
        
        # 按市值加权配置
        for index, row in df.iterrows():
            stock = row['code']
            weight = row['market_cap'] / total_market_cap
            target_value = total_value * weight
            order_target_value(stock, target_value)

# 使用示例
market_cap_weighted_allocation(['600000.XSHG', '600519.XSHG'], context.portfolio.total_value)
```

## 7. 下单风控

### 7.1 检查股票是否可交易

```python
def can_trade(stock):
    """检查股票是否可交易"""
    current_data = get_current_data()
    if stock not in current_data:
        return False
    stock_data = current_data[stock]
    if stock_data.paused:  # 停牌
        return False
    if stock_data.limit_up:  # 涨停
        return False
    if stock_data.limit_down:  # 跌停
        return False
    return True

# 使用示例
if can_trade('600000.XSHG'):
    order('600000.XSHG', 100)
else:
    log.info('股票不可交易')
```

### 7.2 控制下单金额

```python
def limit_order_amount(stock, amount):
    """控制下单金额"""
    current_data = get_current_data()
    if stock in current_data:
        price = current_data[stock].last_price
        order_value = amount * price
        max_order_value = context.portfolio.available_cash * 0.3  # 单次下单不超过可用资金的30%
        if order_value > max_order_value:
            amount = int(max_order_value / price)
            log.info(f'调整下单数量到 {amount}')
        if amount > 0:
            order(stock, amount)

# 使用示例
limit_order_amount('600000.XSHG', 1000)
```


path: examples/momentum_etf_strategy.py
content: # -*- coding: utf-8 -*-
# 动量ETF策略 - 基于聚宽平台

from jqdata import *

class MomentumETFStrategy:
    def __init__(self):
        """初始化策略参数"""
        # ETF池
        self.etf_list = [
            '510050.XSHG',  # 上证50ETF
            '510300.XSHG',  # 沪深300ETF
            '510500.XSHG',  # 中证500ETF
            '510880.XSHG',  # 红利ETF
            '512880.XSHG',  # 银行ETF
            '512660.XSHG',  # 券商ETF
            '512400.XSHG',  # 有色金属ETF
            '512200.XSHG',  # 医药ETF
            '512100.XSHG',  # 中证1000ETF
            '512000.XSHG'   # 计算机ETF
        ]
        # 策略参数
        self.initial_cash = 1000000  # 初始资金
        self.rebalance_period = 21  # 调仓周期（21个交易日，约1个月）
        self.lookback_period = 63  # 动量计算周期（63个交易日，约3个月）
        self.top_n = 3  # 选择表现最好的前3个ETF
    
    def initialize(self, context):
        """
        初始化函数，整个策略生命周期只执行一次
        """
        # 设置基准
        set_benchmark('000300.XSHG')
        # 开启动态复权
        set_option('use_real_price', True)
        # 设置手续费
        set_order_cost(OrderCost(open_tax=0, close_tax=0.001,
                                 open_commission=0.0003, close_commission=0.0003,
                                 close_today_commission=0, min_commission=5), 
                      type='fund')
        
        # 初始化策略参数
        context.etf_list = self.etf_list
        context.initial_cash = self.initial_cash
        context.rebalance_period = self.rebalance_period
        context.lookback_period = self.lookback_period
        context.top_n = self.top_n
        context.trade_count = 0
        
        # 设置定时任务
        run_daily(self.handle_daily, time='9:30')
        
        # 输出日志
        log.info('动量ETF策略初始化完成')
    
    def handle_daily(self, context):
        """
        每日交易逻辑
        """
        context.trade_count += 1
        
        # 达到调仓周期时进行调仓
        if context.trade_count % context.rebalance_period == 0:
            self.rebalance(context)
    
    def rebalance(self, context):
        """
        调仓逻辑：选择动量最强的ETF进行配置
        """
        # 计算每个ETF的动量
        etf_momentum = {}
        for etf in context.etf_list:
            # 使用聚宽特有函数获取历史数据
            df = attribute_history(etf, context.lookback_period, '1d', ['close'])
            if len(df) >= context.lookback_period:
                # 计算动量（收益率）
                momentum = (df['close'][-1] - df['close'][0]) / df['close'][0]
                etf_momentum[etf] = momentum
        
        # 按动量排序，选择表现最好的前N个ETF
        if etf_momentum:
            sorted_etfs = sorted(etf_momentum.items(), key=lambda x: x[1], reverse=True)
            selected_etfs = [item[0] for item in sorted_etfs[:context.top_n]]
            
            # 卖出不在选择列表中的ETF
            for etf in list(context.portfolio.positions.keys()):
                if etf not in selected_etfs:
                    order_target(etf, 0)
                    log.info(f'卖出 {etf}')
            
            # 等权买入选择的ETF
            cash_per_etf = context.portfolio.available_cash / len(selected_etfs)
            for etf in selected_etfs:
                # 检查ETF是否可交易
                current_data = get_current_data()
                if etf in current_data and not current_data[etf].paused:
                    order_value(etf, cash_per_etf)
                    log.info(f'买入 {etf}，金额 {cash_per_etf}，动量 {etf_momentum[etf]:.2%}')
    
    def on_strategy_end(self, context):
        """
        策略结束时执行
        """
        # 计算策略收益
        total_returns = (context.portfolio.total_value - context.initial_cash) / context.initial_cash
        log.info(f'动量ETF策略结束，总收益：{total_returns:.2%}')

# 策略入口
if __name__ == '__main__':
    # 创建策略实例
    strategy = MomentumETFStrategy()
    
    # 运行策略
    run_strategy(
        initialize=strategy.initialize,
        handle_data=strategy.handle_daily,
        start_date='2020-01-01',
        end_date='2023-12-31',
        capital_base=strategy.initial_cash,
        frequency='daily'
    )


path: README.md
content: # joinquant-strategy Skill

帮助在Cursor中编写聚宽(JoinQuant)量化策略的工具集。

## 主要功能
- 提供聚宽策略的标准模板
- 包含常用代码片段
- 提供API快速参考
- 集成最佳实践示例

## 目录结构

```
joinquant-strategy/
├── README.md            # Skill 说明文档
├── templates/           # 策略模板目录
│   ├── basic_template.py            # 基础策略模板
│   ├── dual_strategy_template.py    # 双策略模板
│   └── etf_rotation_template.py     # ETF轮动策略模板
├── snippets/            # 代码片段目录
│   ├── data_fetching.md       # 数据获取常用代码
│   ├── order_handling.md      # 下单处理代码
│   ├── risk_control.md        # 风险控制代码
│   └── technical_analysis.md  # 技术分析代码
├── api_reference/       # API参考文档
│   ├── core_functions.md      # 核心函数说明
│   ├── data_functions.md      # 数据获取函数说明
│   └── order_functions.md     # 下单函数说明
└── examples/            # 示例策略
    └── momentum_etf_strategy.py    # 动量ETF策略
```

## 功能说明

### 策略模板
- **basic_template.py**: 基础策略模板，包含完整的策略结构和基本逻辑
- **dual_strategy_template.py**: 双策略模板，基于示例实现的双策略框架
- **etf_rotation_template.py**: ETF轮动策略模板，用于实现ETF轮动交易策略

### 代码片段
- **data_fetching.md**: 数据获取常用代码，包括历史数据、财务数据等获取方法
- **order_handling.md**: 下单处理代码，包括委托下单、撤单等操作
- **risk_control.md**: 风险控制代码，包括止损、止盈、仓位管理等
- **technical_analysis.md**: 技术分析代码，包括常用技术指标计算

### API参考文档
- **core_functions.md**: 核心函数说明，包括策略初始化、运行等核心功能
- **data_functions.md**: 数据获取函数说明，详细介绍数据API的使用方法
- **order_functions.md**: 下单函数说明，详细介绍下单API的使用方法

### 示例策略
- **momentum_etf_strategy.py**: 动量ETF策略，基于示例整理的完整策略实现

## 使用方法

在Cursor中使用此skill时，可以：

1. **快速开始**：使用`/template`命令选择策略模板
2. **代码补全**：输入`/snippet`查看常用代码片段
3. **API查询**：输入`/api 函数名`快速查看API用法
4. **最佳实践**：参考`examples/`目录学习复杂策略的编写

## 注意事项

- 本工具包提供的代码和模板仅供参考，实际使用时需要根据具体情况进行调整
- 策略开发过程中，应注意风险管理和回测验证
- 请遵守 JoinQuant 平台的相关规定和限制

## 版本信息

- 版本: 1.0.0
- 最后更新: 2026-03-10


path: snippets/data_fetching.md
content: # 数据获取常用代码

## 1. 获取历史行情数据

### 基本用法

```python
# 获取单只股票的历史数据
df = get_price('600000.XSHG', start_date='2020-01-01', end_date='2020-12-31', frequency='daily')

# 获取多只股票的历史数据
df = get_price(['600000.XSHG', '600519.XSHG'], start_date='2020-01-01', end_date='2020-12-31', frequency='daily')

# 获取分钟级数据
df = get_price('600000.XSHG', start_date='2020-01-01', end_date='2020-01-02', frequency='1m')
```

### 聚宽特有函数

```python
# 获取单只股票历史数据（聚宽特有）
df = attribute_history('000001.XSHE', 20, '1d', 
                       ['close', 'volume', 'high', 'low'])

# 获取多只股票历史数据（聚宽特有）
df = history(20, '1d', 'close', 
             ['000001.XSHE', '600000.XSHG'])
```

### 参数说明
- `security`：股票代码，可以是单个代码或代码列表
- `start_date`：开始日期
- `end_date`：结束日期
- `frequency`：数据频率，可选 'daily', '1m', '5m', '15m', '30m', '60m'
- `fields`：返回字段，默认为 ['open', 'close', 'high', 'low', 'volume', 'money']

## 2. 获取财务数据

### 基本用法

```python
# 获取财务指标
df = get_fundamentals(query(indicator), date='2020-12-31')

# 获取估值指标
df = get_fundamentals(query(valuation), date='2020-12-31')

# 获取资产负债表
df = get_fundamentals(query(balance), date='2020-12-31')

# 获取利润表
df = get_fundamentals(query(income), date='2020-12-31')

# 获取现金流量表
df = get_fundamentals(query(cash_flow), date='2020-12-31')
```

### 高级查询

```python
# 筛选条件查询
df = get_fundamentals(
    query(valuation.code, valuation.pe_ratio, indicator.roe)
    .filter(valuation.pe_ratio < 20)
    .filter(indicator.roe > 0.1)
    .order_by(indicator.roe.desc())
    .limit(10),
    date='2020-12-31'
)
```

## 3. 获取股票列表

### 基本用法

```python
# 获取所有股票列表
stock_list = get_all_securities(['stock'], date='2020-01-01')

# 获取沪深300成分股
hs300 = get_index_stocks('000300.XSHG', date='2020-01-01')

# 获取中证500成分股
zz500 = get_index_stocks('000905.XSHG', date='2020-01-01')

# 获取创业板成分股
cyb = get_index_stocks('399006.XSHE', date='2020-01-01')
```

## 4. 获取实时数据

### 基本用法

```python
# 获取实时数据
current_data = get_current_data()

# 获取单只股票的实时价格
price = current_data['600000.XSHG'].last_price

# 获取多只股票的实时价格
for stock in ['600000.XSHG', '600519.XSHG']:
    price = current_data[stock].last_price
    log.info(f'{stock} 当前价格：{price}')
```

## 5. 获取行业数据

### 基本用法

```python
# 获取行业分类
industry = get_industry_stocks('801010', date='2020-01-01')  # 801010 为农林牧渔行业代码

# 获取概念板块
concept = get_concept_stocks('GN001', date='2020-01-01')  # GN001 为融资融券概念代码
```

## 6. 获取宏观经济数据

### 基本用法

```python
# 获取宏观经济指标
df = macro.MAC_INDUSTRY_GDP.get_data()

# 获取利率数据
df = macro.MAC_INTEREST_RATE.get_data()

# 获取汇率数据
df = macro.MAC_EXCHANGE_RATE.get_data()
```

## 7. 数据处理技巧

### 基本用法

```python
# 计算收益率
df['returns'] = df['close'].pct_change()

# 计算移动平均线
df['ma5'] = df['close'].rolling(window=5).mean()
df['ma20'] = df['close'].rolling(window=20).mean()

# 计算成交量指标
df['volume_ma5'] = df['volume'].rolling(window=5).mean()

# 处理缺失值
df = df.dropna()

# 数据合并
df1 = get_price('600000.XSHG', start_date='2020-01-01', end_date='2020-12-31')
df2 = get_price('600519.XSHG', start_date='2020-01-01', end_date='2020-12-31')
merged_df = pd.concat([df1['close'], df2['close']], axis=1, keys=['600000', '600519'])
```

## 8. 获取技术指标

```python
# 使用TA-Lib计算指标
import talib
close_prices = attribute_history('000001.XSHE', 30, '1d', ['close'])['close'].values
sma = talib.SMA(close_prices, timeperiod=20)
```


path: snippets/order_handling.md
content: # 下单处理代码

## 1. 基本下单函数

### 1.1 order 函数

```python
# 买入股票
order('600000.XSHG', 100)  # 买入100股

# 卖出股票
order('600000.XSHG', -100)  # 卖出100股
```

### 1.2 order_target 函数

```python
# 调整持仓到目标数量
order_target('600000.XSHG', 1000)  # 调整持仓到1000股

# 清仓
order_target('600000.XSHG', 0)  # 清仓
```

### 1.3 order_value 函数

```python
# 按金额买入
order_value('600000.XSHG', 10000)  # 买入1万元

# 按金额卖出
order_value('600000.XSHG', -10000)  # 卖出1万元
```

### 1.4 order_target_value 函数

```python
# 调整持仓到目标金额
order_target_value('600000.XSHG', 50000)  # 调整持仓到5万元

# 清仓
order_target_value('600000.XSHG', 0)  # 清仓
```

## 2. 下单参数说明

### 2.1 下单价格

```python
# 限价单
order('600000.XSHG', 100, price=10.5)  # 限价10.5元买入

# 市价单（默认）
order('600000.XSHG', 100)  # 市价买入
```

### 2.2 下单类型

```python
# 买单
order('600000.XSHG', 100)  # 正数为买入

# 卖单
order('600000.XSHG', -100)  # 负数为卖出
```

### 2.3 交易方向

```python
# 开多
order('600000.XSHG', 100)

# 平多
order('600000.XSHG', -100)

# 开空（仅支持期货）
order('IF1901.CCFX', -1)  # 期货开空

# 平空（仅支持期货）
order('IF1901.CCFX', 1)  # 期货平空
```

## 3. 下单结果处理

### 3.1 获取下单结果

```python
# 获取下单结果
order_result = order('600000.XSHG', 100)
if order_result:
    log.info(f'下单成功，订单ID：{order_result.order_id}')
else:
    log.info('下单失败')
```

### 3.2 撤单

```python
# 撤单
order_result = order('600000.XSHG', 100)
if order_result:
    cancel_order(order_result.order_id)  # 撤销订单
```

## 4. 批量下单

### 4.1 批量买入

```python
# 批量买入股票
stock_list = ['600000.XSHG', '600519.XSHG', '000001.XSHE']
cash_per_stock = context.portfolio.available_cash / len(stock_list)

for stock in stock_list:
    order_value(stock, cash_per_stock)
    log.info(f'买入 {stock}，金额 {cash_per_stock}')
```

### 4.2 批量卖出

```python
# 批量卖出股票
for stock in list(context.portfolio.positions.keys()):
    order_target(stock, 0)
    log.info(f'卖出 {stock}')
```

## 5. 下单策略

### 5.1 等权配置

```python
# 等权配置
stock_list = ['600000.XSHG', '600519.XSHG', '000001.XSHE']
target_weight = 1.0 / len(stock_list)

for stock in stock_list:
    target_value = context.portfolio.total_value * target_weight
    order_target_value(stock, target_value)
    log.info(f'调整 {stock} 持仓到 {target_weight:.2%}')
```

### 5.2 按市值加权

```python
# 按市值加权
stock_list = ['600000.XSHG', '600519.XSHG', '000001.XSHE']

# 获取市值数据
df = get_fundamentals(
    query(valuation.code, valuation.market_cap)
    .filter(valuation.code.in_(stock_list)),
    date=context.current_dt.strftime('%Y-%m-%d')
)

# 计算总市值
total_market_cap = df['market_cap'].sum()

# 按市值加权配置
for index, row in df.iterrows():
    stock = row['code']
    weight = row['market_cap'] / total_market_cap
    target_value = context.portfolio.total_value * weight
    order_target_value(stock, target_value)
    log.info(f'调整 {stock} 持仓到 {weight:.2%}')
```

## 6. 下单风控

### 6.1 检查股票是否可交易

```python
# 检查股票是否可交易
def can_trade(stock):
    current_data = get_current_data()
    if stock not in current_data:
        return False
    stock_data = current_data[stock]
    if stock_data.paused:  # 停牌
        return False
    if stock_data.limit_up:  # 涨停
        return False
    if stock_data.limit_down:  # 跌停
        return False
    return True

# 使用示例
stock = '600000.XSHG'
if can_trade(stock):
    order(stock, 100)
else:
    log.info(f'{stock} 不可交易')
```

### 6.2 控制下单金额

```python
# 控制下单金额
def limit_order(stock, amount):
    current_data = get_current_data()
    if stock in current_data:
        price = current_data[stock].last_price
        order_value = amount * price
        max_order_value = context.portfolio.available_cash * 0.3  # 单次下单不超过可用资金的30%
        if order_value > max_order_value:
            amount = int(max_order_value / price)
            log.info(f'调整下单数量到 {amount}')
        if amount > 0:
            order(stock, amount)

# 使用示例
limit_order('600000.XSHG', 1000)
```


path: snippets/risk_control.md
content: # 风险控制代码

## 1. 止损策略

### 1.1 固定百分比止损

```python
# 固定百分比止损
def stop_loss(context, stock, stop_loss_pct=0.05):
    """
    固定百分比止损
    stock: 股票代码
    stop_loss_pct: 止损百分比，默认5%
    """
    position = context.portfolio.positions.get(stock)
    if position:
        cost_price = position.avg_cost
        current_price = get_current_data()[stock].last_price
        loss_pct = (current_price - cost_price) / cost_price
        
        if loss_pct < -stop_loss_pct:
            order_target(stock, 0)
            log.info(f'{stock} 触发止损，亏损 {loss_pct:.2%}')

# 使用示例
for stock in list(context.portfolio.positions.keys()):
    stop_loss(context, stock, stop_loss_pct=0.05)
```

### 1.2 移动止损

```python
# 移动止损
def trailing_stop_loss(context, stock, trailing_stop_pct=0.08):
    """
    移动止损
    stock: 股票代码
    trailing_stop_pct: 移动止损百分比，默认8%
    """
    position = context.portfolio.positions.get(stock)
    if position:
        # 获取历史最高价
        df = get_price(stock, end_date=context.current_dt, count=20, frequency='daily')
        if len(df) > 0:
            highest_price = df['high'].max()
            current_price = get_current_data()[stock].last_price
            
            # 计算止损价格
            stop_price = highest_price * (1 - trailing_stop_pct)
            
            if current_price < stop_price:
                order_target(stock, 0)
                log.info(f'{stock} 触发移动止损，当前价格 {current_price}，止损价格 {stop_price}')

# 使用示例
for stock in list(context.portfolio.positions.keys()):
    trailing_stop_loss(context, stock, trailing_stop_pct=0.08)
```

### 1.3 时间止损

```python
# 时间止损
def time_stop_loss(context, stock, hold_days=10):
    """
    时间止损
    stock: 股票代码
    hold_days: 持有天数，默认10天
    """
    position = context.portfolio.positions.get(stock)
    if position:
        # 计算持有天数
        hold_days_count = (context.current_dt - position.init_time).days
        
        if hold_days_count >= hold_days:
            order_target(stock, 0)
            log.info(f'{stock} 持有时间达到 {hold_days} 天，执行时间止损')

# 使用示例
for stock in list(context.portfolio.positions.keys()):
    time_stop_loss(context, stock, hold_days=10)
```

## 2. 止盈策略

### 2.1 固定百分比止盈

```python
# 固定百分比止盈
def take_profit(context, stock, take_profit_pct=0.1):
    """
    固定百分比止盈
    stock: 股票代码
    take_profit_pct: 止盈百分比，默认10%
    """
    position = context.portfolio.positions.get(stock)
    if position:
        cost_price = position.avg_cost
        current_price = get_current_data()[stock].last_price
        profit_pct = (current_price - cost_price) / cost_price
        
        if profit_pct > take_profit_pct:
            order_target(stock, 0)
            log.info(f'{stock} 触发止盈，盈利 {profit_pct:.2%}')

# 使用示例
for stock in list(context.portfolio.positions.keys()):
    take_profit(context, stock, take_profit_pct=0.1)
```

### 2.2 移动止盈

```python
# 移动止盈
def trailing_take_profit(context, stock, trailing_take_profit_pct=0.05):
    """
    移动止盈
    stock: 股票代码
    trailing_take_profit_pct: 移动止盈百分比，默认5%
    """
    position = context.portfolio.positions.get(stock)
    if position:
        # 获取历史最低价
        df = get_price(stock, end_date=context.current_dt, count=20, frequency='daily')
        if len(df) > 0:
            lowest_price = df['low'].min()
            current_price = get_current_data()[stock].last_price
            
            # 计算止盈价格
            take_price = lowest_price * (1 + trailing_take_profit_pct)
            
            if current_price > take_price:
                order_target(stock, 0)
                log.info(f'{stock} 触发移动止盈，当前价格 {current_price}，止盈价格 {take_price}')

# 使用示例
for stock in list(context.portfolio.positions.keys()):
    trailing_take_profit(context, stock, trailing_take_profit_pct=0.05)
```

## 3. 仓位管理

### 3.1 总仓位控制

```python
# 总仓位控制
def control_total_position(context, max_position_pct=0.8):
    """
    总仓位控制
    max_position_pct: 最大仓位百分比，默认80%
    """
    current_position_pct = context.portfolio.positions_value / context.portfolio.total_value
    
    if current_position_pct > max_position_pct:
        # 需要降低仓位
        reduce_amount = (current_position_pct - max_position_pct) * context.portfolio.total_value
        
        # 按比例减持所有持仓
        for stock in list(context.portfolio.positions.keys()):
            position = context.portfolio.positions[stock]
            reduce_value = reduce_amount * (position.value / context.portfolio.positions_value)
            current_price = get_current_data()[stock].last_price
            reduce_shares = int(reduce_value / current_price)
            
            if reduce_shares > 0:
                order(stock, -reduce_shares)
                log.info(f'降低 {stock} 仓位，减持 {reduce_shares} 股')

# 使用示例
control_total_position(context, max_position_pct=0.8)
```

### 3.2 单只股票仓位控制

```python
# 单只股票仓位控制
def control_single_position(context, stock, max_single_pct=0.2):
    """
    单只股票仓位控制
    stock: 股票代码
    max_single_pct: 单只股票最大仓位百分比，默认20%
    """
    position = context.portfolio.positions.get(stock)
    if position:
        single_position_pct = position.value / context.portfolio.total_value
        
        if single_position_pct > max_single_pct:
            # 需要降低仓位
            target_value = context.portfolio.total_value * max_single_pct
            order_target_value(stock, target_value)
            log.info(f'调整 {stock} 仓位到 {max_single_pct:.2%}')

# 使用示例
for stock in list(context.portfolio.positions.keys()):
    control_single_position(context, stock, max_single_pct=0.2)
```

## 4. 风险指标监控

### 4.1 波动率监控

```python
# 波动率监控
def monitor_volatility(stock, lookback_days=20, max_volatility=0.02):
    """
    波动率监控
    stock: 股票代码
    lookback_days: 回看天数，默认20天
    max_volatility: 最大日波动率，默认2%
    """
    df = get_price(stock, end_date=context.current_dt, count=lookback_days, frequency='daily')
    if len(df) >= lookback_days:
        # 计算日收益率
        df['returns'] = df['close'].pct_change()
        # 计算波动率
        volatility = df['returns'].std()
        
        if volatility > max_volatility:
            log.info(f'{stock} 波动率过高：{volatility:.2%}，超过阈值 {max_volatility:.2%}')
            return False
    return True

# 使用示例
for stock in list(context.portfolio.positions.keys()):
    if not monitor_volatility(stock):
        order_target(stock, 0)
        log.info(f'因波动率过高，卖出 {stock}')
```

### 4.2 夏普比率监控

```python
# 夏普比率监控
def calculate_sharpe_ratio(stock, lookback_days=252, risk_free_rate=0.03):
    """
    计算夏普比率
    stock: 股票代码
    lookback_days: 回看天数，默认252天
    risk_free_rate: 无风险利率，默认3%
    """
    df = get_price(stock, end_date=context.current_dt, count=lookback_days, frequency='daily')
    if len(df) >= lookback_days:
        # 计算日收益率
        df['returns'] = df['close'].pct_change()
        # 计算年化收益率
        annual_return = df['returns'].mean() * 252
        # 计算年化波动率
        annual_volatility = df['returns'].std() * np.sqrt(252)
        # 计算夏普比率
        sharpe_ratio = (annual_return - risk_free_rate) / annual_volatility
        return sharpe_ratio
    return 0

# 使用示例
for stock in list(context.portfolio.positions.keys()):
    sharpe = calculate_sharpe_ratio(stock)
    if sharpe < 0.5:
        log.info(f'{stock} 夏普比率过低：{sharpe:.2f}')
        # 可以考虑减持或卖出
```

## 5. 市场风险控制

### 5.1 大盘风险控制

```python
# 大盘风险控制
def market_risk_control(context, index_code='000300.XSHG', max_drawdown=0.05):
    """
    大盘风险控制
    index_code: 指数代码，默认沪深300
    max_drawdown: 最大回撤，默认5%
    """
    # 获取指数历史数据
    df = get_price(index_code, end_date=context.current_dt, count=30, frequency='daily')
    if len(df) >= 2:
        # 计算指数回撤
        index_high = df['high'].max()
        index_current = df['close'][-1]
        index_drawdown = (index_current - index_high) / index_high
        
        if index_drawdown < -max_drawdown:
            # 大盘回撤过大，降低仓位
            log.info(f'大盘回撤过大：{index_drawdown:.2%}，超过阈值 {max_drawdown:.2%}')
            # 可以选择降低仓位或清仓
            for stock in list(context.portfolio.positions.keys()):
                order_target(stock, 0)
                log.info(f'因大盘风险，卖出 {stock}')

# 使用示例
market_risk_control(context, index_code='000300.XSHG', max_drawdown=0.05)
```

### 5.2 行业风险控制

```python
# 行业风险控制
def industry_risk_control(context, industry_code, max_drawdown=0.08):
    """
    行业风险控制
    industry_code: 行业代码
    max_drawdown: 最大回撤，默认8%
    """
    # 获取行业成分股
    industry_stocks = get_industry_stocks(industry_code, date=context.current_dt.strftime('%Y-%m-%d'))
    
    if industry_stocks:
        # 计算行业平均回撤
        total_drawdown = 0
        valid_stocks = 0
        
        for stock in industry_stocks[:10]:  # 取前10只股票计算
            df = get_price(stock, end_date=context.current_dt, count=30, frequency='daily')
            if len(df) >= 2:
                stock_high = df['high'].max()
                stock_current = df['close'][-1]
                stock_drawdown = (stock_current - stock_high) / stock_high
                total_drawdown += stock_drawdown
                valid_stocks += 1
        
        if valid_stocks > 0:
            avg_drawdown = total_drawdown / valid_stocks
            if avg_drawdown < -max_drawdown:
                log.info(f'行业 {industry_code} 回撤过大：{avg_drawdown:.2%}，超过阈值 {max_drawdown:.2%}')
                # 可以选择减持该行业股票
                for stock in list(context.portfolio.positions.keys()):
                    if stock in industry_stocks:
                        order_target(stock, 0)
                        log.info(f'因行业风险，卖出 {stock}')

# 使用示例
industry_risk_control(context, '801010', max_drawdown=0.08)  # 801010 为农林牧渔行业代码
```


path: snippets/technical_analysis.md
content: # 技术分析代码

## 1. 移动平均线

### 1.1 简单移动平均线 (SMA)

```python
# 计算简单移动平均线
def calculate_sma(stock, period=20):
    """
    计算简单移动平均线
    stock: 股票代码
    period: 周期，默认20
    """
    df = get_price(stock, end_date=context.current_dt, count=period*2, frequency='daily')
    if len(df) >= period:
        df['sma'] = df['close'].rolling(window=period).mean()
        return df['sma'].iloc[-1]
    return None

# 使用示例
sma20 = calculate_sma('600000.XSHG', period=20)
sma50 = calculate_sma('600000.XSHG', period=50)
log.info(f'20日均线：{sma20}，50日均线：{sma50}')

# 金叉死叉判断
if sma20 > sma50:
    log.info('金叉信号')
elif sma20 < sma50:
    log.info('死叉信号')
```

### 1.2 指数移动平均线 (EMA)

```python
# 计算指数移动平均线
def calculate_ema(stock, period=20):
    """
    计算指数移动平均线
    stock: 股票代码
    period: 周期，默认20
    """
    df = get_price(stock, end_date=context.current_dt, count=period*3, frequency='daily')
    if len(df) >= period:
        df['ema'] = df['close'].ewm(span=period, adjust=False).mean()
        return df['ema'].iloc[-1]
    return None

# 使用示例
ema12 = calculate_ema('600000.XSHG', period=12)
ema26 = calculate_ema('600000.XSHG', period=26)
log.info(f'12日EMA：{ema12}，26日EMA：{ema26}')
```

## 2. MACD 指标

```python
# 计算 MACD 指标
def calculate_macd(stock, fast_period=12, slow_period=26, signal_period=9):
    """
    计算 MACD 指标
    stock: 股票代码
    fast_period: 快速周期，默认12
    slow_period: 慢速周期，默认26
    signal_period: 信号周期，默认9
    """
    df = get_price(stock, end_date=context.current_dt, count=slow_period*3, frequency='daily')
    if len(df) >= slow_period:
        # 计算 EMA
        df['ema_fast'] = df['close'].ewm(span=fast_period, adjust=False).mean()
        df['ema_slow'] = df['close'].ewm(span=slow_period, adjust=False).mean()
        # 计算 MACD 线
        df['macd'] = df['ema_fast'] - df['ema_slow']
        # 计算信号线
        df['signal'] = df['macd'].ewm(span=signal_period, adjust=False).mean()
        # 计算柱状图
        df['histogram'] = df['macd'] - df['signal']
        
        return {
            'macd': df['macd'].iloc[-1],
            'signal': df['signal'].iloc[-1],
            'histogram': df['histogram'].iloc[-1]
        }
    return None

# 使用示例
macd_data = calculate_macd('600000.XSHG')
if macd_data:
    log.info(f'MACD: {macd_data["macd"]}, Signal: {macd_data["signal"]}, Histogram: {macd_data["histogram"]}')
    
    # MACD 金叉死叉判断
    if macd_data['macd'] > macd_data['signal']:
        log.info('MACD 金叉信号')
    else:
        log.info('MACD 死叉信号')
```

## 3. RSI 指标

```python
# 计算 RSI 指标
def calculate_rsi(stock, period=14):
    """
    计算 RSI 指标
    stock: 股票代码
    period: 周期，默认14
    """
    df = get_price(stock, end_date=context.current_dt, count=period*2, frequency='daily')
    if len(df) >= period + 1:
        # 计算每日收益率
        df['returns'] = df['close'].pct_change()
        # 计算上涨和下跌
        df['up'] = df['returns'].apply(lambda x: x if x > 0 else 0)
        df['down'] = df['returns'].apply(lambda x: -x if x < 0 else 0)
        # 计算平均上涨和下跌
        df['avg_up'] = df['up'].rolling(window=period).mean()
        df['avg_down'] = df['down'].rolling(window=period).mean()
        # 计算 RSI
        df['rsi'] = 100 - (100 / (1 + df['avg_up'] / df['avg_down']))
        
        return df['rsi'].iloc[-1]
    return None

# 使用示例
rsi = calculate_rsi('600000.XSHG', period=14)
if rsi:
    log.info(f'RSI: {rsi}')
    
    # RSI 超买超卖判断
    if rsi > 70:
        log.info('RSI 超买，可能下跌')
    elif rsi < 30:
        log.info('RSI 超卖，可能上涨')
```

## 4. KDJ 指标

```python
# 计算 KDJ 指标
def calculate_kdj(stock, period=9, k_period=3, d_period=3):
    """
    计算 KDJ 指标
    stock: 股票代码
    period: 周期，默认9
    k_period: K值周期，默认3
    d_period: D值周期，默认3
    """
    df = get_price(stock, end_date=context.current_dt, count=period*3, frequency='daily')
    if len(df) >= period:
        # 计算 RSV
        df['lowest'] = df['low'].rolling(window=period).min()
        df['highest'] = df['high'].rolling(window=period).max()
        df['rsv'] = (df['close'] - df['lowest']) / (df['highest'] - df['lowest']) * 100
        
        # 计算 K 值
        df['k'] = 0
        df['k'].iloc[period-1] = 50  # 初始值
        for i in range(period, len(df)):
            df['k'].iloc[i] = df['k'].iloc[i-1] * (k_period-1)/k_period + df['rsv'].iloc[i] * 1/k_period
        
        # 计算 D 值
        df['d'] = 0
        df['d'].iloc[period-1] = 50  # 初始值
        for i in range(period, len(df)):
            df['d'].iloc[i] = df['d'].iloc[i-1] * (d_period-1)/d_period + df['k'].iloc[i] * 1/d_period
        
        # 计算 J 值
        df['j'] = 3 * df['k'] - 2 * df['d']
        
        return {
            'k': df['k'].iloc[-1],
            'd': df['d'].iloc[-1],
            'j': df['j'].iloc[-1]
        }
    return None

# 使用示例
kdj_data = calculate_kdj('600000.XSHG')
if kdj_data:
    log.info(f'K: {kdj_data["k"]}, D: {kdj_data["d"]}, J: {kdj_data["j"]}')
    
    # KDJ 金叉死叉判断
    if kdj_data['k'] > kdj_data['d']:
        log.info('KDJ 金叉信号')
    else:
        log.info('KDJ 死叉信号')
```

## 5. 布林带

```python
# 计算布林带
def calculate_bollinger_bands(stock, period=20, std_dev=2):
    """
    计算布林带
    stock: 股票代码
    period: 周期，默认20
    std_dev: 标准差倍数，默认2
    """
    df = get_price(stock, end_date=context.current_dt, count=period*2, frequency='daily')
    if len(df) >= period:
        # 计算中轨
        df['middle'] = df['close'].rolling(window=period).mean()
        # 计算标准差
        df['std'] = df['close'].rolling(window=period).std()
        # 计算上轨和下轨
        df['upper'] = df['middle'] + df['std'] * std_dev
        df['lower'] = df['middle'] - df['std'] * std_dev
        
        return {
            'upper': df['upper'].iloc[-1],
            'middle': df['middle'].iloc[-1],
            'lower': df['lower'].iloc[-1]
        }
    return None

# 使用示例
bollinger_data = calculate_bollinger_bands('600000.XSHG')
if bollinger_data:
    current_price = get_current_data()['600000.XSHG'].last_price
    log.info(f'布林带上轨：{bollinger_data["upper"]}，中轨：{bollinger_data["middle"]}，下轨：{bollinger_data["lower"]}')
    
    # 布林带突破判断
    if current_price > bollinger_data['upper']:
        log.info('价格突破布林带上轨，可能继续上涨')
    elif current_price < bollinger_data['lower']:
        log.info('价格突破布林带下轨，可能继续下跌')
```

## 6. 成交量指标

### 6.1 成交量移动平均线

```python
# 计算成交量移动平均线
def calculate_volume_ma(stock, period=20):
    """
    计算成交量移动平均线
    stock: 股票代码
    period: 周期，默认20
    """
    df = get_price(stock, end_date=context.current_dt, count=period*2, frequency='daily')
    if len(df) >= period:
        df['volume_ma'] = df['volume'].rolling(window=period).mean()
        return df['volume_ma'].iloc[-1]
    return None

# 使用示例
volume_ma = calculate_volume_ma('600000.XSHG', period=20)
current_volume = get_price('600000.XSHG', end_date=context.current_dt, count=1, frequency='daily')['volume'].iloc[-1]
log.info(f'成交量：{current_volume}，20日均量：{volume_ma}')

# 量价配合判断
if current_volume > volume_ma * 1.5:
    log.info('成交量放大，可能有资金进入')
```

### 6.2 量比

```python
# 计算量比
def calculate_volume_ratio(stock, days=5):
    """
    计算量比
    stock: 股票代码
    days: 参考天数，默认5
    """
    df = get_price(stock, end_date=context.current_dt, count=days+1, frequency='daily')
    if len(df) >= days+1:
        # 计算当前成交量
        current_volume = df['volume'].iloc[-1]
        # 计算过去N天平均成交量
        avg_volume = df['volume'].iloc[:-1].mean()
        # 计算量比
        volume_ratio = current_volume / avg_volume
        return volume_ratio
    return None

# 使用示例
volume_ratio = calculate_volume_ratio('600000.XSHG', days=5)
if volume_ratio:
    log.info(f'量比：{volume_ratio}')
    if volume_ratio > 2:
        log.info('量比大于2，成交量明显放大')
```

## 7. 其他技术指标

### 7.1 乖离率 (BIAS)

```python
# 计算乖离率
def calculate_bias(stock, period=20):
    """
    计算乖离率
    stock: 股票代码
    period: 周期，默认20
    """
    df = get_price(stock, end_date=context.current_dt, count=period*2, frequency='daily')
    if len(df) >= period:
        # 计算移动平均线
        df['ma'] = df['close'].rolling(window=period).mean()
        # 计算乖离率
        df['bias'] = (df['close'] - df['ma']) / df['ma'] * 100
        return df['bias'].iloc[-1]
    return None

# 使用示例
bias = calculate_bias('600000.XSHG', period=20)
if bias:
    log.info(f'20日乖离率：{bias:.2f}')
    if bias > 10:
        log.info('乖离率过大，可能回调')
    elif bias < -10:
        log.info('乖离率过小，可能反弹')
```

### 7.2 心理线 (PSY)

```python
# 计算心理线
def calculate_psy(stock, period=12):
    """
    计算心理线
    stock: 股票代码
    period: 周期，默认12
    """
    df = get_price(stock, end_date=context.current_dt, count=period+1, frequency='daily')
    if len(df) >= period+1:
        # 计算上涨天数
        df['up'] = df['close'].pct_change() > 0
        up_days = df['up'].iloc[1:].sum()
        # 计算心理线
        psy = up_days / period * 100
        return psy
    return None

# 使用示例
psy = calculate_psy('600000.XSHG', period=12)
if psy:
    log.info(f'心理线：{psy:.2f}')
    if psy > 75:
        log.info('心理线过高，市场可能超买')
    elif psy < 25:
        log.info('心理线过低，市场可能超卖')
```


path: templates/basic_template.py
content: # -*- coding: utf-8 -*-
# 基础策略模板 - 适用于聚宽平台

from jqdata import *

def initialize(context):
    """
    初始化函数，整个策略生命周期只执行一次
    """
    # 设置基准
    set_benchmark('000300.XSHG')
    # 开启动态复权
    set_option('use_real_price', True)
    # 设置手续费
    set_order_cost(OrderCost(open_tax=0, close_tax=0.001,
                             open_commission=0.0003, close_commission=0.0003,
                             close_today_commission=0, min_commission=5), 
                  type='stock')
    
    # 设置滑点
    set_slippage(PriceRelatedSlippage(0.00246))
    
    # 初始化全局变量
    g.security = '000001.XSHE'
    
    # 设置定时任务
    run_daily(market_open, time='9:30')

def market_open(context):
    """
    每日开盘运行函数
    """
    # 获取数据
    df = attribute_history(g.security, 20, '1d', ['close'])
    
    # 计算指标
    ma5 = df['close'].mean()
    current_price = get_current_data()[g.security].last_price
    
    # 交易逻辑
    if current_price > ma5 * 1.01:
        order_value(g.security, context.portfolio.available_cash)
        log.info(f"买入 {g.security}")
    elif current_price < ma5 * 0.99:
        order_target(g.security, 0)
        log.info(f"卖出 {g.security}")


path: templates/dual_strategy_template.py
content: # -*- coding: utf-8 -*-
# 双策略模板 - 支持多子账户管理

from jqdata import *

def initialize(context):
    """
    初始化函数
    """
    # 基础设置
    set_benchmark('000300.XSHG')
    set_option('use_real_price', True)
    
    # 设置多子账户
    g.strategy1_proportion = 0.5  # 策略1资金比例
    g.strategy2_proportion = 0.5  # 策略2资金比例
    
    subportfolios = [
        SubPortfolioConfig(context.portfolio.starting_cash * g.strategy1_proportion, 'stock'),
        SubPortfolioConfig(context.portfolio.starting_cash * g.strategy2_proportion, 'stock'),
    ]
    set_subportfolios(subportfolios)
    
    # 初始化策略
    _init_strategies(context)
    
    # 设置定时任务
    run_daily(strategy1_trade, time='10:00')
    run_daily(strategy2_trade, time='10:30')
    run_daily(after_market_close, time='15:30')

def _init_strategies(context):
    """
    初始化策略对象
    """
    g.strategies = {
        'strategy1': BaseStrategy(context, 0, '策略1'),
        'strategy2': BaseStrategy(context, 1, '策略2')
    }

class BaseStrategy:
    """策略基类"""
    def __init__(self, context, subportfolio_index, name):
        self.subportfolio_index = subportfolio_index
        self.name = name
        self.initial_cash = context.portfolio.subportfolios[subportfolio_index].total_value
        
    def order_target_value(self, context, security, target_value):
        """封装下单函数"""
        return order_target_value_send(context, self.subportfolio_index, 
                                       security, target_value)
    
    def after_market_close(self, context):
        """收盘后处理"""
        sub = context.portfolio.subportfolios[self.subportfolio_index]
        ret = (sub.total_value / self.initial_cash - 1) * 100
        record(**{self.name + '收益率': ret})

def strategy1_trade(context):
    """
    策略1交易逻辑
    """
    strategy = g.strategies['strategy1']
    # 策略1逻辑：动量策略
    stocks = ['600000.XSHG', '600519.XSHG']
    for stock in stocks:
        df = attribute_history(stock, 20, '1d', ['close'])
        if len(df) >= 20:
            returns = (df['close'][-1] - df['close'][0]) / df['close'][0]
            if returns > 0.05:
                sub = context.portfolio.subportfolios[0]
                strategy.order_target_value(context, stock, sub.available_cash)
                log.info(f'策略1买入 {stock}')

def strategy2_trade(context):
    """
    策略2交易逻辑
    """
    strategy = g.strategies['strategy2']
    # 策略2逻辑：均值回归策略
    stocks = ['000001.XSHE', '000858.XSHE']
    for stock in stocks:
        df = attribute_history(stock, 60, '1d', ['close'])
        if len(df) >= 60:
            returns = (df['close'][-1] - df['close'][0]) / df['close'][0]
            if returns < -0.1:
                sub = context.portfolio.subportfolios[1]
                strategy.order_target_value(context, stock, sub.available_cash)
                log.info(f'策略2买入 {stock}')

def after_market_close(context):
    """
    收盘后处理
    """
    for strategy in g.strategies.values():
        strategy.after_market_close(context)


path: templates/etf_rotation_template.py
content: # ETF轮动策略模板

from jqdata import *

class ETFRotationStrategy:
    def __init__(self):
        # 策略参数
        self.etf_list = []  # ETF列表
        self.initial_cash = 1000000  # 初始资金
        self.trade_date = None  # 交易日期
        self.rebalance_period = 20  # 调仓周期（天）
        self.lookback_days = 60  # 回看天数
    
    def initialize(self, context):
        """初始化策略"""
        context.etf_list = self.etf_list
        context.initial_cash = self.initial_cash
        context.rebalance_period = self.rebalance_period
        context.lookback_days = self.lookback_days
        context.trade_count = 0  # 交易计数
        
        # 设置交易频率
        run_daily(self.handle_daily, time='9:30')
        
        # 输出日志
        log.info('ETF轮动策略初始化完成')
    
    def handle_daily(self, context):
        """每日交易逻辑"""
        context.trade_count += 1
        self.trade_date = context.current_dt
        
        # 达到调仓周期时进行调仓
        if context.trade_count % context.rebalance_period == 0:
            self.rebalance(context)
    
    def rebalance(self, context):
        """调仓逻辑"""
        # 计算每个ETF的收益率
        etf_returns = {}
        for etf in context.etf_list:
            # 获取历史数据
            df = get_price(etf, end_date=context.current_dt, count=context.lookback_days, frequency='daily')
            if len(df) < context.lookback_days:
                continue
            
            # 计算收益率
            returns = (df['close'][-1] - df['close'][0]) / df['close'][0]
            etf_returns[etf] = returns
        
        # 按收益率排序，选择表现最好的ETF
        if etf_returns:
            sorted_etfs = sorted(etf_returns.items(), key=lambda x: x[1], reverse=True)
            top_etf = sorted_etfs[0][0]
            
            # 卖出所有持仓
            for stock in list(context.portfolio.positions.keys()):
                order_target(stock, 0)
                log.info(f'卖出 {stock}')
            
            # 买入表现最好的ETF
            cash = context.portfolio.available_cash
            price = get_current_data()[top_etf].last_price
            if cash > 0 and price > 0:
                amount = int(cash / price)
                if amount > 0:
                    order(top_etf, amount)
                    log.info(f'买入 {top_etf}，数量 {amount}，收益率 {etf_returns[top_etf]:.2%}')
    
    def on_strategy_end(self, context):
        """策略结束时执行"""
        # 计算策略收益
        total_returns = (context.portfolio.total_value - context.initial_cash) / context.initial_cash
        log.info(f'ETF轮动策略结束，总收益：{total_returns:.2%}')

# 策略入口
if __name__ == '__main__':
    # 示例用法
    strategy = ETFRotationStrategy()
    # 可以在这里设置策略参数
    strategy.etf_list = ['510050.XSHG', '510300.XSHG', '510500.XSHG']  # 示例ETF
    
    # 运行策略
    run_strategy(
        initialize=strategy.initialize,
        handle_data=strategy.handle_daily,
        start_date='2020-01-01',
        end_date='2020-12-31',
        capital_base=strategy.initial_cash,
        frequency='daily'
    )


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)