加密货币市场基于农历周期的动量量化交易策略

Lunar Calendar Cyclical Trading ETHUSDT Fixed Date Entry/Exit Quantitative Strategy
创建日期: 2025-08-11 09:09:22 最后修改: 2025-08-11 09:09:22
复制: 0 点击次数: 181
avatar of ianzeng123 ianzeng123
2
关注
319
关注者

加密货币市场基于农历周期的动量量化交易策略 加密货币市场基于农历周期的动量量化交易策略

概述

这个策略是一种基于农历日期的加密货币交易方法,通过利用农历周期的特定日期进行买入和卖出操作。该策略从农历新年开始,一直持续到当年阳历12月底,遵循简单的规则:在每个农历月的第5天买入,在每个农历月的第26天卖出。这种方法尝试捕捉可能与农历周期相关的市场模式,为交易者提供了一种结构化且易于遵循的交易框架。该策略考虑了手续费和滑点因素,并使用100%的可用资金进行投资,适用于2020年至2026年的时间范围。

策略原理

该策略的核心原理是基于农历周期对加密货币市场可能存在的影响。代码通过以下方式实现这一理念:

  1. 首先定义各年份的农历新年起始日期和每月天数,覆盖2020年至2026年的周期。
  2. 通过计算当前日期与农历新年之间的天数差,确定当前的农历月份和日期。
  3. 当农历日期为第5天且当前没有持仓时,触发买入信号。
  4. 当农历日期为第26天且持有仓位时,触发卖出信号。
  5. 买入时考虑了滑点和手续费的影响,使用全部可用资金购买尽可能多的加密货币。
  6. 卖出时平掉所有持仓,实现获利或止损。

策略使用精确的日期计算方法,通过数组存储每个农历月的天数,并累计计算从农历新年开始的总天数,从而准确定位当前的农历日期。这种方法保证了交易信号的准确触发。

策略优势

分析该策略的代码,可以总结出以下优势:

  1. 简单明确的规则:固定的买入和卖出日期使策略非常直观,易于理解和执行,减少了交易者的主观判断。
  2. 考虑市场摩擦因素:策略纳入了0.1%的手续费和滑点考虑,使回测结果更接近实际交易环境。
  3. 资金利用效率高:每次交易使用100%的可用资金,最大化了潜在收益。
  4. 考虑农历周期的独特视角:与传统的技术分析不同,该策略引入了农历因素,可能捕捉到与亚洲市场投资者行为相关的独特模式。
  5. 长期适用性:策略提供了从2020年到2026年的农历数据,使交易者可以长期应用这一方法。
  6. 视觉辅助:通过在图表上显示农历日期标签,帮助交易者直观跟踪策略执行情况。
  7. 避免过度交易:每个农历月仅交易一次,降低了过度交易带来的成本和风险。

策略风险

尽管该策略具有上述优势,但也存在一些潜在风险:

  1. 缺乏风险管理机制:策略没有设置止损点,如果市场在买入后大幅下跌,可能导致显著亏损。
  2. 忽略市场趋势和状态:策略仅基于日期进行交易,不考虑市场的整体趋势、波动性或其他技术指标。
  3. 假设周期性规律存在:该策略假设农历周期与加密货币价格存在某种关联,但这种关联可能不稳定或不存在。
  4. 特定时间范围限制:虽然提供了2020-2026年的数据,但未来年份的农历数据需要更新,且策略在此范围外可能无法运行。
  5. 流动性风险:在特定农历日期可能遇到市场流动性问题,尤其是在使用大额资金时。
  6. 日期计算误差可能性:农历日期计算的任何错误都可能导致错误的交易信号。
  7. 缺乏适应性:固定的交易日期无法适应市场条件的变化,可能错过更好的买入或卖出机会。

为了降低这些风险,交易者可以考虑结合其他技术指标进行交易确认,或者设置固定止损位来限制单次交易的损失。

策略优化方向

通过深入分析代码,可以提出以下几个优化方向:

  1. 引入止损机制:添加百分比或绝对金额的止损条件,在亏损达到特定阈值时自动平仓,避免大幅度的损失。优化代码可以增加类似if strategy.position_size > 0 and close < entry_price * (1 - stop_loss_percent)的条件判断。

  2. 融入技术指标确认:结合趋势指标(如移动平均线)或动量指标(如相对强弱指数RSI)作为辅助条件,只在技术指标提供有利信号时执行农历日期交易。这样可以提高信号质量。

  3. 优化买卖日期:通过历史数据回测,分析哪些农历日期对组合实际上提供了最佳的买入和卖出时机,而不是固定使用第5天和第26天。可能某些特定的日期组合表现更佳。

  4. 部分仓位管理:修改策略以使用部分资金而非100%资金进行交易,或根据市场波动性动态调整仓位大小,以分散风险。

  5. 添加市场状态过滤器:在极端市场条件下(如高波动性或明显的熊市趋势)暂停策略执行,避免在不利环境中交易。

  6. 拓展适用时间范围:增加更多年份的农历数据,或开发一个自动计算农历日期的函数,使策略可以无限期运行。

  7. 增加多品种交易:将策略扩展到多个加密货币或其他资产类别,观察农历周期在不同市场中的表现差异。

这些优化方向的实施可以显著提高策略的鲁棒性和适应性,同时保持其简单直观的核心理念。

总结

基于农历周期的加密货币交易策略提供了一种独特的交易视角,利用特定的农历日期进行买入和卖出操作。该策略最大的优势在于其简单明确的规则和易于实施的特点,结合了农历周期这一独特因素,可能捕捉到常规技术分析所忽视的市场模式。

然而,该策略也面临缺乏风险管理和市场适应性的挑战。为了提高策略的有效性,建议引入止损机制、技术指标确认和优化买卖日期等改进措施。这些优化不仅可以减少潜在风险,还能增强策略在不同市场环境下的适应能力。

值得注意的是,任何交易策略都需要经过充分的回测和前向测试,以验证其在实际市场条件下的表现。交易者在采用该策略时,应根据自身风险承受能力和投资目标进行适当调整,并结合其他分析方法做出更全面的交易决策。

策略源码
/*backtest
start: 2024-08-11 00:00:00
end: 2025-08-09 08:00:00
period: 1d
basePeriod: 1d
exchanges: [{"eid":"Futures_Binance","currency":"ETH_USDT"}]
*/

//@version=5
strategy("Lunar ETHUSDT Trading 100% Invest with Fee & Slippage (2020~2026)", overlay=true, commission_type=strategy.commission.percent, commission_value=0.1)

// Fee and slippage settings
feePercent = 0.1    // 0.1%
slippageTicks = 3
tickSize = syminfo.mintick
slippage = slippageTicks * tickSize

// Function for lunar new year start date and monthly lengths by year
f_get_lunar_data() =>
    y = year(time)
    if y == 2020
        [timestamp("Asia/Seoul", 2020, 1, 25, 0, 0), array.from(29,30,29,30,29,30,29,30,29,30,30,29)]
    else if y == 2021
        [timestamp("Asia/Seoul", 2021, 2, 12, 0, 0), array.from(30,29,30,29,30,29,30,29,30,29,30,30)]
    else if y == 2022
        [timestamp("Asia/Seoul", 2022, 2, 1, 0, 0), array.from(29,30,29,30,29,30,29,30,30,29,30,29)]
    else if y == 2023
        [timestamp("Asia/Seoul", 2023, 1, 22, 0, 0), array.from(30,29,30,29,30,29,30,30,29,30,29,30)]
    else if y == 2024
        [timestamp("Asia/Seoul", 2024, 2, 10, 0, 0), array.from(30,29,30,29,30,29,30,29,30,29,30,30,29)]
    else if y == 2025
        [timestamp("Asia/Seoul", 2025, 1, 29, 0, 0), array.from(29,30,29,30,29,30,29,30,30,29,30,29)]
    else if y == 2026
        [timestamp("Asia/Seoul", 2026, 2, 17, 0, 0), array.from(30,29,30,29,30,29,30,30,29,30,29,30)]
    else
        [na, array.new_int()]

// Function to create cumulative monthly days array
f_get_lunar_md(days_arr) =>
    arr = array.new_int()
    sum = 0
    for i = 0 to array.size(days_arr) - 1
        sum += array.get(days_arr, i)
        array.push(arr, sum)
    arr

// Get lunar start date and monthly lengths
[ts_start, lunar_lengths] = f_get_lunar_data()
valid = not na(ts_start)
days_since = valid ? math.floor((time - ts_start) / 86400000) : na
cumulative = valid ? f_get_lunar_md(lunar_lengths) : na

// Declare lunar month, day, last day variables
var int lunar_month = na
var int lunar_day = na
var int lunar_last_day = na

// Calculate lunar date
if valid and not na(days_since) and days_since >= 0
    lunar_month := na
    lunar_day := na
    lunar_last_day := na
    for i = 0 to array.size(cumulative) - 1
        cum = array.get(cumulative, i)
        prev = i == 0 ? 0 : array.get(cumulative, i - 1)
        if days_since < cum
            lunar_month := i + 1
            lunar_day := days_since - prev + 1
            lunar_last_day := array.get(lunar_lengths, i)
            break
else
    lunar_month := na
    lunar_day := na
    lunar_last_day := na

// Buy condition: Lunar day 5 and no current position
buy_condition = not na(lunar_day) and lunar_day == 5 and strategy.position_size == 0

// Sell condition: Lunar day 26 and holding position
sell_condition = not na(lunar_day) and lunar_day == 26 and strategy.position_size > 0

// Buy/sell price adjusted for slippage and fee
price_buy = close + slippage
price_buy_with_fee = price_buy * (1 + feePercent * 0.01)

price_sell = close - slippage
price_sell_with_fee = price_sell * (1 - feePercent * 0.01)

// Calculate buy quantity using 100% of equity
qty = math.floor(strategy.equity / price_buy_with_fee)

// Buy order (limit)
if buy_condition and qty > 0
    strategy.entry("Lunar Buy", strategy.long, qty, limit=price_buy)

// Sell order (close all)
if sell_condition and strategy.position_size > 0
    strategy.close("Lunar Buy")

// True range variable (for label position adjustment)
tr = ta.tr(true)

// Date format creation
yr = year(time)
mo = month(time)
dy = dayofmonth(time)
mo_str = mo < 10 ? "0" + str.tostring(mo) : str.tostring(mo)
dy_str = dy < 10 ? "0" + str.tostring(dy) : str.tostring(dy)
solar_str = str.tostring(yr) + "-" + mo_str + "-" + dy_str

// Display solar and lunar date and position label (on bar close)
if barstate.islastconfirmedhistory and not na(lunar_day)
    label.new(bar_index, high - tr * 6,  "Solar: " + solar_str + "\nLunar: " + str.tostring(lunar_month) + "-" + str.tostring(lunar_day) ,
      style=label.style_label_up, size=size.normal, color=color.new(color.teal, 50), textcolor=color.white)

// Display "15" label at bottom on lunar day 15 (lowest of last 50 bars - 1 true range)
if not na(lunar_day) and lunar_day == 15
    low_offset = ta.lowest(low, 50) - tr
    label.new(bar_index, low_offset, "15", style=label.style_label_down, color=color.orange, textcolor=color.white, size=size.normal)
相关推荐