在股票技术分析中,除了常见的MACD、KDJ等指标外,BIAS乖离率也是一个非常实用的摆动类指标。它通过计算当前股价与某条移动平均线之间的偏离百分比,来判断股价短期是否过度偏离其平均成本,从而提示超买或超卖风险。对于iOS开发者来说,用Core Graphics自绘K线图不仅能完全掌控渲染细节,还能灵活叠加各类技术指标。本文将完整讲解如何在iOS端实现BIAS指标的计算、绘制与预警逻辑。

一、BIAS乖离率的计算原理与公式推导
BIAS指标的中文全称是乖离率,其核心思想非常直观:股价会围绕移动平均线上下波动,但当偏离幅度过大时,市场存在回归均线的内在动力。BIAS的计算公式为:BIAS = (当日收盘价 - N日移动平均价) / N日移动平均价 × 100%。当结果为正值时,说明股价位于均线之上,数值越大表示短期涨幅透支越严重,市场处于超买状态;当结果为负值时,说明股价位于均线之下,负得越多表示超卖越严重,反弹概率随之增大。
在实际开发中,通常采用双周期组合来增强判断的可靠性,例如同时计算6日BIAS和12日BIAS。6日BIAS波动灵敏,适合捕捉短线极端偏离;12日BIAS相对平滑,用于确认中期趋势的偏离程度。计算N日均线最常见的方法是简单移动平均(SMA),即把最近N天的收盘价求和后除以N,每前进一步就滑动一个交易日重新计算。下面是Objective-C实现的均线与BIAS计算代码:
// 计算N日简单移动平均线,返回与K线数组等长的数组,前N-1个为NSNull
- (NSArray *)calculateMA:(NSArray *)closePrices period:(NSInteger)period {
NSMutableArray *result = [NSMutableArray array];
CGFloat sum = 0;
for (NSInteger i = 0; i < closePrices.count; i++) {
CGFloat close = [closePrices[i] floatValue];
sum += close;
if (i >= period - 1) {
[result addObject:@(sum / period)];
// 滑动窗口:减去窗口最左侧的值
sum -= [closePrices[i - period + 1] floatValue];
} else {
[result addObject:[NSNull null]];
}
}
return result;
}
// 计算BIAS乖离率数组,period为均线周期
- (NSArray *)calculateBIAS:(NSArray *)closePrices period:(NSInteger)period {
NSArray *maArray = [self calculateMA:closePrices period:period];
NSMutableArray *biasArray = [NSMutableArray array];
for (NSInteger i = 0; i < closePrices.count; i++) {
if ([maArray[i] isKindOfClass:[NSNull class]]) {
[biasArray addObject:[NSNull null]];
continue;
}
CGFloat close = [closePrices[i] floatValue];
CGFloat ma = [maArray[i] floatValue];
CGFloat bias = (close - ma) / ma * 100.0;
[biasArray addObject:@(bias)];
}
return biasArray;
}需要注意一个细节:前period-1个交易日由于数据不足,均线无法计算,BIAS值也应标记为无效。绘制时必须跳过这些无效点,否则会在图表左侧产生错误的锯齿线。另外,除权除息日的跳空会导致乖离率出现异常尖刺,如果是对精度要求较高的场景,建议先对价格序列做前复权处理再计算。
二、Core Graphics绘制架构设计与主副图布局
一个专业的K线图通常采用主图加副图的分层结构:主图绘制K线蜡烛和均线,副图绘制BIAS曲线。整体渲染建议封装在一个继承自UIView的自定义视图里,重写drawRect:方法完成绘制。为了让滚动和长按十字光标交互流畅,建议将指标计算与路径生成分离——数据变化时先算好BIAS数组并缓存,drawRect:里只做坐标映射和路径描画,避免每帧重复计算均线。
坐标映射是自绘图表的关键环节。主图价格区间和副图BIAS区间的Y轴映射必须分别计算。BIAS的取值范围理论上是负无穷到正无穷,但实际行情中大多落在-15到15之间,因此可以取当前可视区域内BIAS的最大最小值并上下留出5%的边距作为绘图区间,也可以直接固定为±20,让不同页面之间的指标形态具有可比性。下面是双阶段绘制的核心代码:
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat mainHeight = rect.size.height * 0.65; // 主图占65%
CGFloat subTop = mainHeight + 1; // 分隔线
CGFloat subHeight = rect.size.height - mainHeight - 1;
// 绘制主副图分隔线
CGContextSetStrokeColorWithColor(context, [UIColor colorWithWhite:0.15 alpha:1].CGColor);
CGContextStrokeRect(context, CGRectMake(0, mainHeight, rect.size.width, 1));
[self drawCandlesInContext:context rect:CGRectMake(0, 0, rect.size.width, mainHeight)];
[self drawBIASInContext:context rect:CGRectMake(0, subTop, rect.size.width, subHeight)];
}
// 将BIAS值映射为副图Y坐标
- (CGFloat)yForBias:(CGFloat)bias inRect:(CGRect)rect {
CGFloat maxBias = 20, minBias = -20; // 固定区间,可根据数据自适应
CGFloat ratio = (bias - minBias) / (maxBias - minBias);
return rect.origin.y + (1 - ratio) * rect.size.height; // 注意Y轴方向翻转
}这里有个新手容易踩的坑:UIKit的坐标系Y轴向下,而BIAS数值越大应该越靠上,所以映射时要用1减去比例做翻转,忘记这一步会导致曲线上下颠倒。另外调用setNeedsDisplay触发重绘时,应只在数据真正变化时调用,配合CADisplayLink做十字光标跟随可以显著降低CPU占用。
三、BIAS曲线绘制与超买超卖区域标记
BIAS曲线本身用CGPathRef逐点连线即可,6日BIAS与12日BIAS用不同颜色区分。真正提升可读性的是超买超卖参考线与色块区域:在+5位置画一条红色虚线标记超买阈值,在-5位置画一条绿色虚线标记超卖阈值,并将超出阈值的曲线段用背景色块高亮。这种视觉处理能让使用者一眼看出极端偏离区间。
实现色块标记的思路是:遍历BIAS数组,找到连续超过阈值的区间,把这段区间的曲线与阈值线围成的封闭区域填充半透明颜色。具体代码如下:
- (void)drawBIASInContext:(CGContextRef)context rect:(CGRect)rect {
// 先画超买超卖参考线
CGFloat colors[4] = {1,0,0,1};
[self drawRefLineAtBias:5.0 color:[UIColor redColor]
context:context rect:rect label:@"超买 +5"];
[self drawRefLineAtBias:-5.0 color:[UIColor greenColor]
context:context rect:rect label:@"超卖 -5"];
// 超买区域色块:BIAS曲线与+5线围成的区域填充红色半透明
CGContextSaveGState(context);
CGMutablePathRef fillPath = CGPathCreateMutable();
BOOL started = NO;
NSInteger startIndex = 0;
for (NSInteger i = 0; i < self.biasArray.count; i++) {
if ([self.biasArray[i] isKindOfClass:[NSNull class]]) continue;
CGFloat bias = [self.biasArray[i] floatValue];
CGFloat x = [self xForIndex:i];
CGFloat y = [self yForBias:bias inRect:rect];
CGFloat yRef = [self yForBias:5.0 inRect:rect];
if (bias > 5.0) {
if (!started) {
CGPathMoveToPoint(fillPath, NULL, x, yRef);
startIndex = i;
started = YES;
}
CGPathAddLineToPoint(fillPath, NULL, x, y);
} else if (started) {
// 区间结束:闭合路径并填充
CGPathAddLineToPoint(fillPath, NULL, x, yRef);
CGPathCloseSubpath(fillPath);
started = NO;
}
}
CGContextSetFillColorWithColor(context,
[UIColor colorWithRed:1 green:0 blue:0 alpha:0.18].CGColor);
CGContextAddPath(context, fillPath);
CGContextFillPath(context);
CGPathRelease(fillPath);
CGContextRestoreGState(context);
// 最后绘制BIAS折线
// ...省略逐点连线部分
}超买超卖阈值的选取并非固定不变。经验上,6日BIAS在强势市场可取+8到-8,震荡市取+5到-5;如果同时要求6日BIAS和12日BIAS同向突破阈值,信号会更加可靠但频率会降低。建议把阈值做成可配置属性,方便用户根据不同标的的波动特性调整。此外,绘制文字标签时用drawAtPoint:withAttributes:即可,注意在retina屏幕上这些UIKit文本方法会自动处理缩放,无需手动乘scale。
四、股价偏离均线的预警机制实现
光有图形还不够,实时行情场景中更需要主动预警。预警逻辑可以封装为一个独立的检测方法:每当新K线数据到达时,重新计算最新一根K线的BIAS值,与预设阈值比较,一旦突破就通过代理回调或本地通知告知上层。为了减少噪音,可以加入持续确认条件,例如要求BIAS连续3根K线站上阈值才触发,或者要求6日与12日BIAS形成金叉式共振。
typedef NS_ENUM(NSInteger, BiasAlertType) {
BiasAlertNone,
BiasAlertOverbought, // 超买预警
BiasAlertOversold // 超卖预警
};
- (void)checkBiasAlertWithHandler:(void(^)(BiasAlertType type, CGFloat bias))handler {
if (self.biasArray.count < 3) return;
CGFloat latest = [self.biasArray.lastObject floatValue];
CGFloat threshold = self.alertThreshold; // 例如5.0
if (latest > threshold) {
// 连续确认:检查前两根是否也超阈值,避免单日尖刺误报
CGFloat prev1 = [self.biasArray[self.biasArray.count-2] floatValue];
CGFloat prev2 = [self.biasArray[self.biasArray.count-3] floatValue];
if (prev1 > threshold && prev2 > threshold) {
handler(BiasAlertOverbought, latest);
return;
}
}
if (latest < -threshold) {
handler(BiasAlertOversold, latest);
return;
}
handler(BiasAlertNone, latest);
}预警消息可以叠加本地通知:UNUserNotificationCenter配合UNMutableNotificationContent推送提醒,内容格式建议带上股票代码、当前BIAS数值和触发方向,例如股价偏离6日均线达+6.2%,注意回落风险。同时在K线图上为触发预警的那根蜡烛打一个标记气泡,图形与通知互相印证,用户体验会好很多。
五、性能优化与工程化建议
当K线数据达到几千根并支持手势缩放时,绘制性能会成为瓶颈。几个实用的优化手段:第一,指标数组随数据增量更新而非全量重算,新K线到来时只需追加一个值;第二,只绘制可视区域内的数据点,通过setVisibleRange:记录起止索引,绘制循环直接跳过区间外的数据;第三,K线蜡烛可以考虑用CALayer或CAShapeLayer分层缓存,把不经常变化的主图和不频繁重绘的BIAS曲线放在不同图层,缩放时只重绘必要部分。
工程化方面,建议把均线计算、BIAS计算、坐标映射拆分为独立的纯逻辑类,与视图层解耦,这样单元测试可以直接验证数值正确性,例如用一组已知收盘价验证BIAS结果与手算一致。绘制层只依赖输入数组,后续如果要扩展MACD、RSI等其他副图指标,只需新增计算器和对应的绘制方法即可,整体架构保持清晰可扩展。最后提醒一点,BIAS指标在单边趋势行情中容易长期钝化,超买不等于马上回调,实战中应结合成交量、均线多头排列等条件综合研判,代码层面也应保留阈值与确认周期的配置入口,让策略可以灵活调优。
Core GraphicsBIAS乖离率iOS K线图绘制修改时间:2026-08-31 22:35:01