黄金价格急涨急跌判断函数(C#版)



准备写一个黄金价格急涨急跌判断函数,功能也简单,短时间内黄金价格上涨或下跌幅度达到指定值即认为发生了急涨或急跌,但这中间要考虑到小波动(如上图示),和豆包来回沟通了几次,第一版代码如下:

/// <summary>
/// 判断金价是否出现急涨
/// </summary>
/// <param name="prices">金价数组,按时间从旧到新排列(索引0最早,索引Length-1最新)</param>
/// <param name="sharpRiseThreshold">急涨阈值:最新价与前面某价格的差值≥此值则视为急涨</param>
/// <param name="maxPullbackTolerance">反向下跌容忍阈值:连续下跌区间的头尾最大差值</param>
/// <returns>是否急涨</returns>
public static bool IsGoldPriceSharpRise(decimal[] prices, decimal sharpRiseThreshold, decimal maxPullbackTolerance)
{
    if (prices == null || prices.Length < 2)
        return false;

    decimal latestPrice = prices[prices.Length - 1];
    int i = prices.Length - 2;

    while (i >= 0)
    {
        // 1. 先检查当前价格是否已经满足急涨条件
        decimal riseAmount = latestPrice - prices[i];
        if (riseAmount >= sharpRiseThreshold)
        {
            return true;
        }

        // 2. 检测是否进入连续下跌区间
        if (prices[i] > prices[i + 1])
        {
            // 记录下跌区间的右端点(最晚时间,区间最低点位置)
            int pullbackEndIndex = i + 1;

            // 继续往左找,直到下跌趋势结束
            i--;
            while (i >= 0 && prices[i] >= prices[i + 1])
            {
                i--;
            }

            // 此时i+1是下跌区间的左端点(最早时间,区间最高点位置)
            int pullbackStartIndex = i + 1;
            decimal pullbackHigh = prices[pullbackStartIndex];
            decimal pullbackLow = prices[pullbackEndIndex];
            decimal totalPullback = pullbackHigh - pullbackLow;

            // 3. 检查整个下跌区间的总跌幅是否超过容忍度
            if (totalPullback > maxPullbackTolerance)
            {
                return false;
            }

            // 下跌区间处理完毕,继续往前遍历
            continue;
        }

        // 没有下跌,继续往前推
        i--;
    }

    // 遍历完所有价格都未达到急涨条件
    return false;
}

/// <summary>
/// 判断金价是否出现急跌
/// </summary>
/// <param name="prices">金价数组,按时间从旧到新排列(索引0最早,索引Length-1最新)</param>
/// <param name="sharpDropThreshold">急跌阈值:前面某价格与最新价的差值≥此值则视为急跌</param>
/// <param name="maxReboundTolerance">反向上涨容忍阈值:连续上涨区间的头尾最大差值</param>
/// <returns>是否急跌</returns>
public static bool IsGoldPriceSharpDrop(decimal[] prices, decimal sharpDropThreshold, decimal maxReboundTolerance)
{
    if (prices == null || prices.Length < 2)
        return false;

    decimal latestPrice = prices[prices.Length - 1];
    int i = prices.Length - 2;

    while (i >= 0)
    {
        // 1. 先检查当前价格是否已经满足急跌条件
        decimal dropAmount = prices[i] - latestPrice;
        if (dropAmount >= sharpDropThreshold)
        {
            return true;
        }

        // 2. 检测是否进入连续上涨区间(急跌时的反向趋势)
        if (prices[i] < prices[i + 1])
        {
            // 记录上涨区间的右端点(最晚时间,区间最高点位置)
            int reboundEndIndex = i + 1;

            // 继续往左找,直到上涨趋势结束
            i--;
            while (i >= 0 && prices[i] <= prices[i + 1])
            {
                i--;
            }

            // 此时i+1是上涨区间的左端点(最早时间,区间最低点位置)
            int reboundStartIndex = i + 1;
            decimal reboundLow = prices[reboundStartIndex];
            decimal reboundHigh = prices[reboundEndIndex];
            decimal totalRebound = reboundHigh - reboundLow;

            // 3. 检查整个上涨区间的总涨幅是否超过容忍度
            if (totalRebound > maxReboundTolerance)
            {
                return false;
            }

            // 上涨区间处理完毕,继续往前遍历
            continue;
        }

        // 没有上涨,继续往前推
        i--;
    }

    // 遍历完所有价格都未达到急跌条件
    return false;
}



不久我又想到新的情况,比如判断急涨时(如上图),每一次反向时都没有超过容忍阈值,但可能发生多次反向,最终加起来可能远远超过容忍阈值了,便又有了新的想法,记录下每次反向的高值和低值,最后再看一下最高值和最低值的差值是不是超过容忍阈值,我和豆包说了想法,豆包提出了瑕疵,比如下图这种情形:



最低值在最高值前面,从最低到最高其实是上涨的,我用它们的差值做判断显然不合适,于是又改进了下,得到最高值后,再从其右边找最低值(它不一定是所有低值中的最低值),解决了目前能想到问题,最终版本如下:

using System;
using System.Collections.Generic;

public class GoldPriceAnalyzer
{
    #region 内部实体
    private class PullbackInterval
    {
        public decimal High { get; set; }
        public decimal Low { get; set; }
        public int HighIndex { get; set; }
    }

    private class ReboundInterval
    {
        public decimal Low { get; set; }
        public decimal High { get; set; }
        public int LowIndex { get; set; }
    }
    #endregion

    #region IsGoldPriceSharpRise 方法组
    /// <summary>
    /// 判断金价是否急涨(数组版)
    /// </summary>
    /// <param name="prices">金价数组,按时间从旧到新排列</param>
    /// <param name="sharpRiseThreshold">急涨阈值</param>
    /// <param name="maxPullbackTolerance">反向下跌容忍阈值</param>
    /// <returns>是否急涨</returns>
    public static bool IsGoldPriceSharpRise(decimal[] prices, decimal sharpRiseThreshold, decimal maxPullbackTolerance)
    {
        if (prices == null)
            throw new ArgumentNullException("prices", "金价数组不能为null");

        if (prices.Length < 2)
            return false;

        decimal latestPrice = prices[prices.Length - 1];
        int i = prices.Length - 2;
        List<PullbackInterval> pullbacks = new List<PullbackInterval>();

        while (i >= 0)
        {
            decimal riseAmount = latestPrice - prices[i];
            if (riseAmount >= sharpRiseThreshold)
            {
                return FinalValidateSharpRise(pullbacks, maxPullbackTolerance);
            }

            if (prices[i] > prices[i + 1])
            {
                int pullbackEndIndex = i + 1;
                i--;
                while (i >= 0 && prices[i] >= prices[i + 1])
                {
                    i--;
                }

                int pullbackStartIndex = i + 1;
                decimal pullbackHigh = prices[pullbackStartIndex];
                decimal pullbackLow = prices[pullbackEndIndex];
                decimal totalPullback = pullbackHigh - pullbackLow;

                if (totalPullback > maxPullbackTolerance)
                {
                    return false;
                }

                pullbacks.Add(new PullbackInterval
                {
                    High = pullbackHigh,
                    Low = pullbackLow,
                    HighIndex = pullbackStartIndex
                });

                continue;
            }

            i--;
        }

        return false;
    }

    /// <summary>
    /// 判断金价是否急涨(List版重载)
    /// </summary>
    /// <param name="prices">金价数组,按时间从旧到新排列</param>
    /// <param name="sharpRiseThreshold">急涨阈值</param>
    /// <param name="maxPullbackTolerance">反向下跌容忍阈值</param>
    /// <returns></returns>
    public static bool IsGoldPriceSharpRise(List<decimal> prices, decimal sharpRiseThreshold, decimal maxPullbackTolerance)
    {
        if (prices == null)
            throw new ArgumentNullException("prices", "金价列表不能为null");

        return IsGoldPriceSharpRise(prices.ToArray(), sharpRiseThreshold, maxPullbackTolerance);
    }
    #endregion

    #region IsGoldPriceSharpDrop 方法组
    /// <summary>
    /// 判断金价是否急跌(数组版)
    /// </summary>
    /// <param name="prices">金价数组,按时间从旧到新排列</param>
    /// <param name="sharpDropThreshold">急跌阈值</param>
    /// <param name="maxReboundTolerance">反向上涨容忍阈值</param>
    /// <returns>是否急跌</returns>
    public static bool IsGoldPriceSharpDrop(decimal[] prices, decimal sharpDropThreshold, decimal maxReboundTolerance)
    {
        if (prices == null)
            throw new ArgumentNullException("prices", "金价数组不能为null");

        if (prices.Length < 2)
            return false;

        decimal latestPrice = prices[prices.Length - 1];
        int i = prices.Length - 2;
        List<ReboundInterval> rebounds = new List<ReboundInterval>();

        while (i >= 0)
        {
            decimal dropAmount = prices[i] - latestPrice;
            if (dropAmount >= sharpDropThreshold)
            {
                return FinalValidateSharpDrop(rebounds, maxReboundTolerance);
            }

            if (prices[i] < prices[i + 1])
            {
                int reboundEndIndex = i + 1;
                i--;
                while (i >= 0 && prices[i] <= prices[i + 1])
                {
                    i--;
                }

                int reboundStartIndex = i + 1;
                decimal reboundLow = prices[reboundStartIndex];
                decimal reboundHigh = prices[reboundEndIndex];
                decimal totalRebound = reboundHigh - reboundLow;

                if (totalRebound > maxReboundTolerance)
                {
                    return false;
                }

                rebounds.Add(new ReboundInterval
                {
                    Low = reboundLow,
                    High = reboundHigh,
                    LowIndex = reboundStartIndex
                });

                continue;
            }

            i--;
        }

        return false;
    }

    /// <summary>
    /// 判断金价是否急跌(List版重载)
    /// </summary>
    /// <param name="prices">金价数组,按时间从旧到新排列</param>
    /// <param name="sharpDropThreshold">急跌阈值</param>
    /// <param name="maxReboundTolerance">反向上涨容忍阈值</param>
    /// <returns></returns>
    public static bool IsGoldPriceSharpDrop(List<decimal> prices, decimal sharpDropThreshold, decimal maxReboundTolerance)
    {
        if (prices == null)
            throw new ArgumentNullException("prices", "金价列表不能为null");

        return IsGoldPriceSharpDrop(prices.ToArray(), sharpDropThreshold, maxReboundTolerance);
    }
    #endregion

    #region 内部验证方法
    /// <summary>
    /// 急涨最终验证逻辑
    /// </summary>
    /// <param name="pullbacks"></param>
    /// <param name="maxTolerance"></param>
    /// <returns></returns>
    private static bool FinalValidateSharpRise(List<PullbackInterval> pullbacks, decimal maxTolerance)
    {
        if (pullbacks.Count == 0)
            return true;

        PullbackInterval maxHighInterval = pullbacks[0];
        foreach (PullbackInterval interval in pullbacks)
        {
            if (interval.High > maxHighInterval.High)
                maxHighInterval = interval;
        }

        decimal minLowAfterMaxHigh = maxHighInterval.Low;
        foreach (PullbackInterval interval in pullbacks)
        {
            if (interval.HighIndex > maxHighInterval.HighIndex)
            {
                if (interval.Low < minLowAfterMaxHigh)
                    minLowAfterMaxHigh = interval.Low;
            }
        }

        decimal totalMaxPullback = maxHighInterval.High - minLowAfterMaxHigh;
        return totalMaxPullback < maxTolerance;
    }

    /// <summary>
    /// 急跌最终验证逻辑(与急涨完全对称)
    /// </summary>
    /// <param name="rebounds"></param>
    /// <param name="maxTolerance"></param>
    /// <returns></returns>
    private static bool FinalValidateSharpDrop(List<ReboundInterval> rebounds, decimal maxTolerance)
    {
        if (rebounds.Count == 0)
            return true;

        ReboundInterval minLowInterval = rebounds[0];
        foreach (ReboundInterval interval in rebounds)
        {
            if (interval.Low < minLowInterval.Low)
                minLowInterval = interval;
        }

        decimal maxHighAfterMinLow = minLowInterval.High;
        foreach (ReboundInterval interval in rebounds)
        {
            if (interval.LowIndex > minLowInterval.LowIndex)
            {
                if (interval.High > maxHighAfterMinLow)
                    maxHighAfterMinLow = interval.High;
            }
        }

        decimal totalMaxRebound = maxHighAfterMinLow - minLowInterval.Low;
        return totalMaxRebound < maxTolerance;
    }
    #endregion
}


上一篇: 别再乱加 try-catch 了!7 个异常处理策略,让你的程序再也不崩溃
下一篇: 这是最新一篇日志
文章来自: 本站原创
引用通告: 查看所有引用 | 我要引用此文章
Tags:
最新日志:
评论: 0 | 引用: 0 | 查看次数: 37
发表评论
登录后再发表评论!