上传文件至「/」

This commit is contained in:
2026-07-11 20:13:35 +00:00
commit ac03d9e67b
5 changed files with 767 additions and 0 deletions
+223
View File
@@ -0,0 +1,223 @@
# 截面策略使用指南
## 概述
截面策略(Cross-Sectional Strategy)是一种同时交易多个标的的策略类型。它根据某些因子对所有标的进行评分和排序,然后做多排名靠前的标的,做空排名靠后的标的。
## 功能特点
1. **多标的支持**:可以同时交易多个标的(股票、币种等)
2. **自动排序**:根据指标计算的评分自动排序标的
3. **组合管理**:自动管理持仓组合,保持做多/做空比例
4. **定期调仓**:支持每日/每周/每月调仓频率
5. **批量执行**:并行执行多个标的的交易,提高效率
## 配置说明
### 策略配置参数
**交易助手 → 创建策略** 中选择「截面策略」,自选标的池并配置组合参数;也可在 API/数据库的 `trading_config` 中直接写入以下字段:
```json
{
"cs_strategy_type": "cross_sectional", // 策略类型:'single' 或 'cross_sectional'
"symbol_list": [ // 标的列表
"Crypto:BTC/USDT",
"Crypto:ETH/USDT",
"Crypto:BNB/USDT"
],
"portfolio_size": 10, // 持仓组合大小(做多+做空的总数)
"long_ratio": 0.5, // 做多比例(0-1之间,0.5表示50%做多,50%做空)
"rebalance_frequency": "daily" // 调仓频率:'daily' | 'weekly' | 'monthly'
}
```
### 参数说明
- **cs_strategy_type**:
- `'single'`: 单标的策略(默认,原有功能)
- `'cross_sectional'`: 截面策略
- **symbol_list**:
- 标的列表,格式为 `["Market:SYMBOL", ...]`
- 例如:`["Crypto:BTC/USDT", "Crypto:ETH/USDT"]`
- **portfolio_size**:
- 持仓组合大小,即同时持有的标的数量
- 例如:10 表示同时持有10个标的
- **long_ratio**:
- 做多比例,0-1之间的浮点数
- 例如:0.5 表示50%做多,50%做空
- 例如:1.0 表示100%做多(不做空)
- **rebalance_frequency**:
- 调仓频率
- `'daily'`: 每日调仓
- `'weekly'`: 每周调仓
- `'monthly'`: 每月调仓
## 指标代码编写
截面策略的指标代码需要返回所有标的的评分和排序。
### 指标代码模板
```python
# 截面策略指标模板
# 输入:data = {symbol1: df1, symbol2: df2, ...}
# 输出:scores = {symbol1: score1, symbol2: score2, ...}
# rankings = [symbol1, symbol2, ...] # 可选,如果不提供会根据scores自动排序
scores = {}
for symbol, df in data.items():
# 计算每个标的的因子值
# 例如:动量因子
momentum = (df['close'].iloc[-1] / df['close'].iloc[-20] - 1) * 100
# 例如:RSI指标
def calculate_rsi(prices, period=14):
delta = prices.diff()
gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
return rsi.iloc[-1]
rsi = calculate_rsi(df['close'], 14)
# 综合评分(可以根据需要调整权重)
score = momentum * 0.6 + (100 - rsi) * 0.4
scores[symbol] = score
# 可选:手动指定排序(如果不提供,系统会根据scores自动排序)
# rankings = sorted(scores.keys(), key=lambda x: scores[x], reverse=True)
```
### 指标代码环境变量
在指标代码执行时,可以使用以下变量:
- `symbols`: 标的列表 `['Crypto:BTC/USDT', 'Crypto:ETH/USDT', ...]`
- `data`: 所有标的的K线数据 `{symbol: df, ...}`
- `scores`: 用于存储评分的字典(需要在代码中填充)
- `rankings`: 用于存储排序的列表(可选,如果不提供会根据scores自动排序)
- `np`: numpy
- `pd`: pandas
- `trading_config`: 交易配置
- `config`: 交易配置(别名)
### 输出要求
指标代码需要填充 `scores` 字典:
```python
scores[symbol] = score_value # score_value 可以是任意数值
```
可选:填充 `rankings` 列表(如果不提供,系统会根据scores自动排序):
```python
rankings = [symbol1, symbol2, ...] # 按评分从高到低排序
```
## 信号生成逻辑
系统会根据以下逻辑自动生成交易信号:
1. **排序标的**:根据指标计算的评分对所有标的进行排序
2. **选择持仓**
- 排名靠前的 `portfolio_size * long_ratio` 个标的 → 做多
- 排名靠后的 `portfolio_size * (1 - long_ratio)` 个标的 → 做空
3. **生成信号**
- 新增标的:如果标的不在当前持仓中,生成开仓信号
- 移除标的:如果标的不在目标持仓中,生成平仓信号
- 方向变更:如果标的需要从多转空或从空转多,先生成平仓信号,再生成开仓信号
## 使用示例
### 1. 创建截面策略
通过API创建策略时,在请求体中包含:
```json
{
"strategy_name": "动量截面策略",
"trading_config": {
"cs_strategy_type": "cross_sectional",
"symbol_list": [
"Crypto:BTC/USDT",
"Crypto:ETH/USDT",
"Crypto:BNB/USDT",
"Crypto:ADA/USDT",
"Crypto:SOL/USDT"
],
"portfolio_size": 5,
"long_ratio": 0.6,
"rebalance_frequency": "daily",
"timeframe": "1H",
"initial_capital": 10000,
"leverage": 1,
"market_type": "swap"
},
"indicator_config": {
"indicator_id": 123,
"indicator_code": "..."
}
}
```
### 2. 指标代码示例
```python
# 动量+RSI综合评分
scores = {}
for symbol, df in data.items():
# 20周期动量
momentum = (df['close'].iloc[-1] / df['close'].iloc[-20] - 1) * 100
# RSI
delta = df['close'].diff()
gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
rsi_value = rsi.iloc[-1]
# 综合评分
score = momentum * 0.7 + (100 - rsi_value) * 0.3
scores[symbol] = score
```
## 注意事项
1. **数据获取**:系统会为每个标的获取K线数据,如果某个标的数据获取失败,会跳过该标的
2. **调仓频率**:系统会根据 `rebalance_frequency` 设置检查是否需要调仓,未到调仓时间时不会执行交易
3. **批量执行**:所有交易信号会并行执行,最多同时执行10个交易
4. **持仓管理**:系统会自动管理持仓,确保持仓组合符合配置要求
5. **兼容性**:截面策略功能不影响现有的单标的策略,两者可以共存
## 数据库迁移
如果需要使用数据库字段存储截面策略配置(可选),可以运行迁移脚本:
```sql
-- 运行 migrations/add_cross_sectional_strategy.sql
```
如果不运行迁移脚本,截面策略配置会存储在 `trading_config` JSON字段中,功能完全正常。
## 故障排查
1. **策略不执行**
- 检查 `cs_strategy_type` 是否为 `'cross_sectional'`
- 检查 `symbol_list` 是否不为空
- 检查调仓频率是否已到时间
2. **指标执行失败**
- 检查指标代码是否正确填充 `scores` 字典
- 检查所有标的的数据是否都能正常获取
3. **信号不生成**
- 检查 `portfolio_size` 是否小于等于 `symbol_list` 的长度
- 检查评分是否有效
+223
View File
@@ -0,0 +1,223 @@
# Cross-Sectional Strategy Guide
## Overview
Cross-Sectional Strategy is a strategy type that trades multiple symbols simultaneously. It scores and ranks all symbols based on certain factors, then goes long on top-ranked symbols and short on bottom-ranked symbols.
## Features
1. **Multi-Symbol Support**: Can trade multiple symbols (stocks, cryptocurrencies, etc.) simultaneously
2. **Automatic Ranking**: Automatically ranks symbols based on indicator-calculated scores
3. **Portfolio Management**: Automatically manages portfolio positions, maintaining long/short ratios
4. **Periodic Rebalancing**: Supports daily/weekly/monthly rebalancing frequencies
5. **Batch Execution**: Executes trades for multiple symbols in parallel for improved efficiency
## Configuration
### Strategy Configuration Parameters
When creating or editing a strategy, add the following parameters to `trading_config`:
```json
{
"cs_strategy_type": "cross_sectional", // Strategy type: 'single' or 'cross_sectional'
"symbol_list": [ // Symbol list
"Crypto:BTC/USDT",
"Crypto:ETH/USDT",
"Crypto:BNB/USDT"
],
"portfolio_size": 10, // Portfolio size (total of long + short positions)
"long_ratio": 0.5, // Long ratio (0-1, 0.5 means 50% long, 50% short)
"rebalance_frequency": "daily" // Rebalancing frequency: 'daily' | 'weekly' | 'monthly'
}
```
### Parameter Description
- **cs_strategy_type**:
- `'single'`: Single-symbol strategy (default, original functionality)
- `'cross_sectional'`: Cross-sectional strategy
- **symbol_list**:
- List of symbols, format: `["Market:SYMBOL", ...]`
- Example: `["Crypto:BTC/USDT", "Crypto:ETH/USDT"]`
- **portfolio_size**:
- Portfolio size, i.e., the number of symbols to hold simultaneously
- Example: 10 means holding 10 symbols at the same time
- **long_ratio**:
- Long ratio, a float between 0 and 1
- Example: 0.5 means 50% long, 50% short
- Example: 1.0 means 100% long (no short positions)
- **rebalance_frequency**:
- Rebalancing frequency
- `'daily'`: Daily rebalancing
- `'weekly'`: Weekly rebalancing
- `'monthly'`: Monthly rebalancing
## Indicator Code Writing
Cross-sectional strategy indicator code needs to return scores and rankings for all symbols.
### Indicator Code Template
```python
# Cross-sectional strategy indicator template
# Input: data = {symbol1: df1, symbol2: df2, ...}
# Output: scores = {symbol1: score1, symbol2: score2, ...}
# rankings = [symbol1, symbol2, ...] # Optional, auto-sorted by scores if not provided
scores = {}
for symbol, df in data.items():
# Calculate factor values for each symbol
# Example: Momentum factor
momentum = (df['close'].iloc[-1] / df['close'].iloc[-20] - 1) * 100
# Example: RSI indicator
def calculate_rsi(prices, period=14):
delta = prices.diff()
gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
return rsi.iloc[-1]
rsi = calculate_rsi(df['close'], 14)
# Composite score (adjust weights as needed)
score = momentum * 0.6 + (100 - rsi) * 0.4
scores[symbol] = score
# Optional: Manually specify ranking (if not provided, system will auto-sort by scores)
# rankings = sorted(scores.keys(), key=lambda x: scores[x], reverse=True)
```
### Indicator Code Environment Variables
The following variables are available when indicator code executes:
- `symbols`: Symbol list `['Crypto:BTC/USDT', 'Crypto:ETH/USDT', ...]`
- `data`: K-line data for all symbols `{symbol: df, ...}`
- `scores`: Dictionary for storing scores (needs to be populated in code)
- `rankings`: List for storing rankings (optional, auto-sorted by scores if not provided)
- `np`: numpy
- `pd`: pandas
- `trading_config`: Trading configuration
- `config`: Trading configuration (alias)
### Output Requirements
Indicator code needs to populate the `scores` dictionary:
```python
scores[symbol] = score_value # score_value can be any numeric value
```
Optional: Populate the `rankings` list (if not provided, system will auto-sort by scores):
```python
rankings = [symbol1, symbol2, ...] # Sorted by score from high to low
```
## Signal Generation Logic
The system automatically generates trading signals based on the following logic:
1. **Rank Symbols**: Rank all symbols based on indicator-calculated scores
2. **Select Positions**:
- Top `portfolio_size * long_ratio` symbols → Long
- Bottom `portfolio_size * (1 - long_ratio)` symbols → Short
3. **Generate Signals**:
- New symbols: If a symbol is not in current positions, generate open signal
- Remove symbols: If a symbol is not in target positions, generate close signal
- Direction change: If a symbol needs to change from long to short or vice versa, first generate close signal, then open signal
## Usage Examples
### 1. Create Cross-Sectional Strategy
When creating a strategy via API, include in the request body:
```json
{
"strategy_name": "Momentum Cross-Sectional Strategy",
"trading_config": {
"cs_strategy_type": "cross_sectional",
"symbol_list": [
"Crypto:BTC/USDT",
"Crypto:ETH/USDT",
"Crypto:BNB/USDT",
"Crypto:ADA/USDT",
"Crypto:SOL/USDT"
],
"portfolio_size": 5,
"long_ratio": 0.6,
"rebalance_frequency": "daily",
"timeframe": "1H",
"initial_capital": 10000,
"leverage": 1,
"market_type": "swap"
},
"indicator_config": {
"indicator_id": 123,
"indicator_code": "..."
}
}
```
### 2. Indicator Code Example
```python
# Momentum + RSI Composite Score
scores = {}
for symbol, df in data.items():
# 20-period momentum
momentum = (df['close'].iloc[-1] / df['close'].iloc[-20] - 1) * 100
# RSI
delta = df['close'].diff()
gain = (delta.where(delta > 0, 0)).rolling(window=14).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=14).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
rsi_value = rsi.iloc[-1]
# Composite score
score = momentum * 0.7 + (100 - rsi_value) * 0.3
scores[symbol] = score
```
## Notes
1. **Data Retrieval**: The system retrieves K-line data for each symbol. If data retrieval fails for a symbol, that symbol will be skipped.
2. **Rebalancing Frequency**: The system checks if rebalancing is needed based on `rebalance_frequency` settings. No trades will be executed if it's not time to rebalance.
3. **Batch Execution**: All trading signals are executed in parallel, with a maximum of 10 concurrent trades.
4. **Position Management**: The system automatically manages positions to ensure the portfolio meets configuration requirements.
5. **Compatibility**: Cross-sectional strategy functionality does not affect existing single-symbol strategies. Both can coexist.
## Database Migration
If you need to store cross-sectional strategy configuration in database fields (optional), you can run the migration script:
```sql
-- Run migrations/add_cross_sectional_strategy.sql
```
If you don't run the migration script, cross-sectional strategy configuration will be stored in the `trading_config` JSON field, and functionality will work normally.
## Troubleshooting
1. **Strategy Not Executing**:
- Check if `cs_strategy_type` is `'cross_sectional'`
- Check if `symbol_list` is not empty
- Check if rebalancing frequency time has been reached
2. **Indicator Execution Failed**:
- Check if indicator code correctly populates the `scores` dictionary
- Check if data for all symbols can be retrieved normally
3. **Signals Not Generated**:
- Check if `portfolio_size` is less than or equal to the length of `symbol_list`
- Check if scores are valid
+162
View File
@@ -0,0 +1,162 @@
# QuantDinger Extension Guide
This guide explains how to add features without making the backend harder to
maintain. Prefer small, boring, easy-to-review changes.
## Before You Start
1. Find the closest existing module.
2. Read `docs/ARCHITECTURE.md` and `docs/MODULE_BOUNDARIES.md`.
3. Decide whether the change is API, service, adapter, data, worker, or docs.
4. Keep route paths and response fields backward-compatible unless a breaking
change is explicitly approved.
## Add a Human Web API Endpoint
Use this flow for routes consumed by the web or mobile UI:
1. Add the route in the closest route module, or create a small sibling module
if the current file is already large.
2. Keep the route thin: validate input, call a service, return JSON.
3. Put workflow logic in `app/services/<feature>/...` when the feature has more
than one workflow. Use a flat service file only for small one-off helpers.
4. Add or update OpenAPI tag metadata in `app/openapi/register.py` and
`app/openapi/tags.py` when introducing a new route family.
5. Regenerate the human API spec:
```bash
cd backend_api_python
python scripts/export_openapi.py
```
6. Run the OpenAPI smoke test when dependencies are available:
```bash
cd backend_api_python
python -m pytest tests/test_openapi.py -q
```
## Add an Agent Gateway Endpoint
Use this flow for external AI agents, MCP clients, and automation:
1. Add code under `app/routes/agent_v1`.
2. Enforce token scopes using the existing agent security helpers.
3. Keep write/trade actions explicit and auditable.
4. Update `docs/agent/agent-openapi.json`.
5. Do not mix agent-only routes into the human OpenAPI spec.
## Add a Market Data Source
1. Implement the adapter in `app/data_sources`.
2. Return normalized rows:
```python
{
"time": 1710000000,
"open": 1.0,
"high": 1.2,
"low": 0.9,
"close": 1.1,
"volume": 1000.0,
}
```
3. Register selection logic in `DataSourceFactory`.
4. Keep provider-specific column names inside the adapter.
5. Include market, symbol, timeframe, exchange, and market type in cache keys
where relevant.
6. Add a small smoke check or script if the provider has fragile symbol rules.
## Add a Symbol Master Data Source
1. Prefer database-backed master data over hardcoded symbol lists.
2. Put sync/import logic in scripts or service modules, not route files.
3. Keep seed SQL deterministic so fresh Docker installs work offline.
4. Make search tolerant of symbol, name, alias, and localized company names.
5. Do not hardcode large symbol lists in Python route modules.
## Add an Exchange or Broker Adapter
1. Put low-level API calls under `app/services/live_trading`.
2. Normalize account, position, order, fill, and error shapes.
3. Keep exchange precision and sizing logic near the adapter.
4. Keep strategy lifecycle outside the adapter.
5. Add explicit notes for market type support:
- spot
- swap/perpetual
- US stock
- paper/live
6. If an adapter supports live orders, document idempotency and retry behavior.
## Add a Strategy Runtime Feature
1. Avoid adding more unrelated logic to `trading_executor.py`.
2. Extract new behavior into a focused service module first.
3. Keep order intent creation separate from order execution.
4. Keep market data reads separate from account mutation.
5. Add safeguards for duplicate starts, duplicate orders, and worker restarts.
## Add a Backtest Feature
1. Avoid growing `backtest.py` unless the change is tiny.
2. Prefer extracting:
- data loading
- signal evaluation
- execution model
- metrics
- report formatting
3. Preserve historical result compatibility.
4. Clearly document fill assumptions.
## Add an AI Feature
1. Keep prompts and skill definitions separate from provider calls.
2. Keep provider adapters behind a service boundary.
3. Localize user-facing text through translation/i18n structures.
4. Do not let AI flows place live orders without explicit existing trade APIs,
permissions, billing checks, and audit logs.
5. For streaming routes, handle cancellation and partial failures.
## Add Settings
1. Define the setting in the backend settings registry.
2. Choose whether it is public, admin-only, or internal.
3. Keep secrets out of public config endpoints.
4. If the setting changes OpenAPI behavior, regenerate `docs/api/openapi.yaml`.
5. Keep environment variable names stable and documented.
## Add Background Work
1. Put startup wiring in `app/startup.py`.
2. Put worker behavior in a dedicated service module.
3. Add a clear owner/lock model for multi-process deployments.
4. Make work idempotent before adding retries.
5. Log start, stop, retry, and failure states in English.
## Verification Checklist
Run the smallest useful checks for the change:
```bash
python -m py_compile path/to/changed_file.py
python scripts/check_mojibake.py
```
For API changes:
```bash
cd backend_api_python
python scripts/export_openapi.py
python -m pytest tests/test_openapi.py -q
```
For Docker/deploy changes:
```bash
docker compose config
```
For frontend-only work, use the frontend dev server. Do not run production
builds during every small iteration unless the change touches bundling,
environment injection, or release assets.
+70
View File
@@ -0,0 +1,70 @@
# ============================================================
# 截面策略指标示例(研究参考版)
# Cross-Sectional Strategy Indicator Example (Research Reference)
# Momentum + RSI Composite Score
# ============================================================
#
# 使用方法:
# 1. 作为截面研究思路示例阅读
# 2. 用于理解“对多个标的打分再排序”的基本写法
#
# 当前限制:
# - 策略快照回测(cross_sectional)暂不支持;实盘指标截面策略可在「交易助手」创建
# - 创建时选择「截面策略」并插入模板,或在本指标中实现 scores 字典
#
# 评分逻辑:
# - 动量因子(20周期):价格变化率,越高越好
# - RSI 指标(14周期):反转 RSI 值,越低越好(100 - RSI
# - 综合评分:70% 动量 + 30% RSI 反转值
#
# ============================================================
# 截面策略指标
# 输入: data = {symbol1: df1, symbol2: df2, ...}
# 输出: scores = {symbol1: score1, symbol2: score2, ...}
scores = {}
# 遍历全部标的
for symbol, df in data.items():
# 确保数据长度足够
if len(df) < 20:
scores[symbol] = 0
continue
# === 1. 计算动量因子 (20周期) ===
# 动量 = (当前价格 / 20周期前价格 - 1) * 100
momentum = (df['close'].iloc[-1] / df['close'].iloc[-20] - 1) * 100
# === 2. 计算RSI指标 (14周期) ===
def calculate_rsi(prices, period=14):
"""计算 RSI 指标"""
delta = prices.diff()
gain = (delta.where(delta > 0, 0)).rolling(window=period).mean()
loss = (-delta.where(delta < 0, 0)).rolling(window=period).mean()
rs = gain / loss
rsi = 100 - (100 / (1 + rs))
return rsi.iloc[-1]
rsi_value = calculate_rsi(df['close'], 14)
# === 3. 综合评分 ===
# 动量越高 = 评分越高
# RSI越低(超卖)= 评分越高(100 - RSI)
# 权重: 70% 动量 + 30% RSI反转值
momentum_score = momentum
rsi_score = 100 - rsi_value # 反转 RSI(RSI 越低,评分越高)
composite_score = momentum_score * 0.7 + rsi_score * 0.3
scores[symbol] = composite_score
# === 可选:手动指定排序 ===
# 如果不提供,系统会根据 scores 自动排序
# rankings = sorted(scores.keys(), key=lambda x: scores[x], reverse=True)
# === 研究语义说明 ===
# 1. 根据评分对所有标的进行排序(从高到低)
# 2. 选择排名靠前的 N 个标的做多(基于 portfolio_size * long_ratio
# 3. 选择排名靠后的 N 个标的做空(基于 portfolio_size * (1 - long_ratio)
# 4. 在未来平台链路完善后,可由系统统一生成买入 / 卖出 / 平仓动作
+89
View File
@@ -0,0 +1,89 @@
# ============================================================
# 双均线策略(文档同步版 · 四路信号)
# Dual Moving Average Strategy (Doc-Aligned, Four-Way)
# ============================================================
#
# 适用场景:
# 1. 在 Indicator IDE 中快速验证均线交叉逻辑
# 2. 演示 `# @param` + `# @strategy` + 四路执行列
# 3. 与平台默认模板 / SIGNAL_EXECUTION_STANDARD v1 对齐
#
# 注意:
# - 杠杆请在产品面板中设置,不要写进源码
# - 触及型 tp/sl 请用 close_*,勿与 trailingEnabled 叠加
#
# ============================================================
my_indicator_name = "双均线交叉策略"
my_indicator_description = "EMA 金叉/死叉四路信号,边缘触发;退出由引擎 stopLoss/takeProfit 管理。"
# --- QuantDinger execution contract (v1) ---
# signal_form: four_way
# exit_owner: engine
# flip_mode: R2
# === 参数声明(供前端、AI 调参与代码质量检查识别) ===
# @param sma_short int 14 短期均线周期
# @param sma_long int 28 长期均线周期
# === 平台默认策略配置 ===
# @strategy stopLossPct 0.02
# @strategy takeProfitPct 0.05
# @strategy entryPct 0.25
# @strategy trailingEnabled false
# @strategy tradeDirection both
# 说明:close_* 只表达均线反转平仓;固定止损/止盈由 engine 风控负责。
# 如果改成触及型 TP/SL,请同步改为 exit_owner: indicator。
def edge(s):
s = s.fillna(False).astype(bool)
return s & ~s.shift(1).fillna(False)
sma_short_period = int(params.get("sma_short", 14))
sma_long_period = int(params.get("sma_long", 28))
df = df.copy()
sma_short = df["close"].rolling(sma_short_period).mean()
sma_long = df["close"].rolling(sma_long_period).mean()
golden = (sma_short > sma_long) & (sma_short.shift(1) <= sma_long.shift(1))
death = (sma_short < sma_long) & (sma_short.shift(1) >= sma_long.shift(1))
df["open_long"] = edge(golden)
df["open_short"] = edge(death)
df["close_long"] = edge(death)
df["close_short"] = edge(golden)
n = len(df)
open_long_marks = [
df["low"].iloc[i] * 0.995 if bool(df["open_long"].iloc[i]) else None for i in range(n)
]
open_short_marks = [
df["high"].iloc[i] * 1.005 if bool(df["open_short"].iloc[i]) else None for i in range(n)
]
output = {
"name": my_indicator_name,
"plots": [
{
"name": f"SMA{sma_short_period}",
"data": sma_short.fillna(0).tolist(),
"color": "#FF9800",
"overlay": True,
},
{
"name": f"SMA{sma_long_period}",
"data": sma_long.fillna(0).tolist(),
"color": "#3F51B5",
"overlay": True,
},
],
"signals": [
{"type": "buy", "text": "L", "data": open_long_marks, "color": "#00E676"},
{"type": "sell", "text": "S", "data": open_short_marks, "color": "#FF5252"},
],
}