You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

592 lines
26 KiB

// ChartViewController.m
// HC
//
// Created by huilinLi on 2025/11/27.
//
#import "ChartViewController.h"
#import "StockKLineModel.h"
@interface ChartViewController ()
// 上方卡片容器
@property (nonatomic, strong) UIView *cardContainer;
// k线选择项容器
@property (nonatomic, strong) UIView *kSelectContainer;
// K线图表容器
@property (nonatomic, strong) UIView *kLineContainer;
// K线图表滚动视图
@property (nonatomic, strong) UIScrollView *kLineScrollView;
// K线数据
@property (nonatomic, strong) NSArray *kLineData;
// 价格刻度标签
@property (nonatomic, strong) NSArray *priceLabels;
// 日期刻度标签
@property (nonatomic, strong) NSArray *dateLabels;
// 默认展示的K线数量
@property (nonatomic, assign) NSInteger visibleKLineCount;
@end
@implementation ChartViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor blackColor];
self.visibleKLineCount = 40;
[self generateMockData];
[self setupSubviews];
[self setupConstraints];
dispatch_async(dispatch_get_main_queue(), ^{
[self drawKLineChart];
if (self.kLineScrollView.contentSize.width > self.kLineScrollView.bounds.size.width) {
CGPoint offset = CGPointMake(self.kLineScrollView.contentSize.width - self.kLineScrollView.bounds.size.width, 0);
[self.kLineScrollView setContentOffset:offset animated:NO];
}
});
}
#pragma mark - UI Setup
-(void) setupSubviews{
// 卡片容器
_cardContainer = [[UIView alloc] init];
_cardContainer.backgroundColor = [UIColor colorWithRed:26.0/255.0
green:26.0/255.0
blue:2.0/255.0
alpha:1.0];
_cardContainer.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:_cardContainer];
UILabel *cardLabel1 = [[UILabel alloc] init];
cardLabel1.text = @"1617.060";
cardLabel1.textColor = [UIColor redColor];
cardLabel1.font = [UIFont systemFontOfSize:15];
cardLabel1.translatesAutoresizingMaskIntoConstraints = NO;
[_cardContainer addSubview:cardLabel1];
UILabel *cardLabel2 = [[UILabel alloc] init];
cardLabel2.text = @"-7.470";
cardLabel2.textColor = [UIColor redColor];
cardLabel2.font = [UIFont systemFontOfSize:8];
cardLabel2.translatesAutoresizingMaskIntoConstraints = NO;
[_cardContainer addSubview:cardLabel2];
UILabel *cardLabel3 = [[UILabel alloc] init];
cardLabel3.text = @"-0.461%";
cardLabel3.textColor = [UIColor redColor];
cardLabel3.font = [UIFont systemFontOfSize:8];
cardLabel3.translatesAutoresizingMaskIntoConstraints = NO;
[_cardContainer addSubview:cardLabel3];
[NSLayoutConstraint activateConstraints:@[
[cardLabel1.topAnchor constraintEqualToAnchor:_cardContainer.topAnchor constant:25],
[cardLabel1.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:10],
[cardLabel2.topAnchor constraintEqualToAnchor:_cardContainer.topAnchor constant:50],
[cardLabel2.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant: 8],
[cardLabel3.topAnchor constraintEqualToAnchor:_cardContainer.topAnchor constant:50],
[cardLabel3.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant: 45]
]];
UILabel *cardLabelHeight = [[UILabel alloc] init];
cardLabelHeight.text = @"";
cardLabelHeight.textColor = [UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1];
cardLabelHeight.font = [UIFont systemFontOfSize:15];
cardLabelHeight.translatesAutoresizingMaskIntoConstraints = NO;
[_cardContainer addSubview:cardLabelHeight];
UILabel *cardLabelStart = [[UILabel alloc] init];
cardLabelStart.text = @"";
cardLabelStart.textColor = [UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1];
cardLabelStart.font = [UIFont systemFontOfSize:15];
cardLabelStart.translatesAutoresizingMaskIntoConstraints = NO;
[_cardContainer addSubview:cardLabelStart];
UILabel *cardLabelVolume = [[UILabel alloc] init];
cardLabelVolume.text = @"";
cardLabelVolume.textColor = [UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1];
cardLabelVolume.font = [UIFont systemFontOfSize:15];
cardLabelVolume.translatesAutoresizingMaskIntoConstraints = NO;
[_cardContainer addSubview:cardLabelVolume];
UILabel *cardLabelLow = [[UILabel alloc] init];
cardLabelLow.text = @"";
cardLabelLow.textColor = [UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1];
cardLabelLow.font = [UIFont systemFontOfSize:15];
cardLabelLow.translatesAutoresizingMaskIntoConstraints = NO;
[_cardContainer addSubview:cardLabelLow];
UILabel *cardLabelChange = [[UILabel alloc] init];
cardLabelChange.text = @"";
cardLabelChange.textColor = [UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1];
cardLabelChange.font = [UIFont systemFontOfSize:15];
cardLabelChange.translatesAutoresizingMaskIntoConstraints = NO;
[_cardContainer addSubview:cardLabelChange];
UILabel *cardLabelQuota = [[UILabel alloc] init];
cardLabelQuota.text = @"";
cardLabelQuota.textColor = [UIColor colorWithRed:0.2 green:0.2 blue:0.2 alpha:1];
cardLabelQuota.font = [UIFont systemFontOfSize:15];
cardLabelQuota.translatesAutoresizingMaskIntoConstraints = NO;
[_cardContainer addSubview:cardLabelQuota];
UILabel *cardNumHeight = [[UILabel alloc] init];
cardNumHeight.text = @"1617.260";
cardNumHeight.textColor = [UIColor redColor];
cardNumHeight.font = [UIFont systemFontOfSize:12];
cardNumHeight.translatesAutoresizingMaskIntoConstraints = NO;
[_cardContainer addSubview:cardNumHeight];
UILabel *cardNumStart = [[UILabel alloc] init];
cardNumStart.text = @"1616.750";
cardNumStart.textColor = [UIColor redColor];
cardNumStart.font = [UIFont systemFontOfSize:12];
cardNumStart.translatesAutoresizingMaskIntoConstraints = NO;
[_cardContainer addSubview:cardNumStart];
UILabel *cardNumVolume = [[UILabel alloc] init];
cardNumVolume.text = @"1.511亿";
cardNumVolume.textColor = [UIColor whiteColor];
cardNumVolume.font = [UIFont systemFontOfSize:12];
cardNumVolume.translatesAutoresizingMaskIntoConstraints = NO;
[_cardContainer addSubview:cardNumVolume];
UILabel *cardNumLow = [[UILabel alloc] init];
cardNumLow.text = @"1608.850";
cardNumLow.textColor = [UIColor whiteColor];
cardNumLow.font = [UIFont systemFontOfSize:12];
cardNumLow.translatesAutoresizingMaskIntoConstraints = NO;
[_cardContainer addSubview:cardNumLow];
UILabel *cardNumChange = [[UILabel alloc] init];
cardNumChange.text = @"--";
cardNumChange.textColor = [UIColor whiteColor];
cardNumChange.font = [UIFont systemFontOfSize:12];
cardNumChange.translatesAutoresizingMaskIntoConstraints = NO;
[_cardContainer addSubview:cardNumChange];
UILabel *cardNumQuota = [[UILabel alloc] init];
cardNumQuota.text = @"--";
cardNumQuota.textColor = [UIColor whiteColor];
cardNumQuota.font = [UIFont systemFontOfSize:12];
cardNumQuota.translatesAutoresizingMaskIntoConstraints = NO;
[_cardContainer addSubview:cardNumQuota];
[NSLayoutConstraint activateConstraints:@[
[cardLabelHeight.bottomAnchor constraintEqualToAnchor:_cardContainer.bottomAnchor constant:-75],
[cardLabelStart.bottomAnchor constraintEqualToAnchor:cardLabelHeight.bottomAnchor],
[cardLabelVolume.bottomAnchor constraintEqualToAnchor:cardLabelHeight.bottomAnchor],
[cardLabelLow.topAnchor constraintEqualToAnchor:_cardContainer.bottomAnchor constant:-50],
[cardLabelLow.leadingAnchor constraintEqualToAnchor:cardLabelHeight.leadingAnchor],
[cardLabelChange.topAnchor constraintEqualToAnchor:_cardContainer.bottomAnchor constant:-50],
[cardLabelChange.leadingAnchor constraintEqualToAnchor:cardLabelStart.leadingAnchor],
[cardLabelQuota.topAnchor constraintEqualToAnchor:_cardContainer.bottomAnchor constant:-50],
[cardLabelQuota.leadingAnchor constraintEqualToAnchor:cardLabelVolume.leadingAnchor],
[cardNumHeight.bottomAnchor constraintEqualToAnchor:_cardContainer.topAnchor constant:45],
[cardNumHeight.leadingAnchor constraintEqualToAnchor:cardLabelHeight.leadingAnchor],
[cardNumStart.bottomAnchor constraintEqualToAnchor:cardNumHeight.bottomAnchor],
[cardNumStart.leadingAnchor constraintEqualToAnchor:cardLabelStart.leadingAnchor],
[cardNumVolume.bottomAnchor constraintEqualToAnchor:cardNumHeight.bottomAnchor],
[cardNumVolume.leadingAnchor constraintEqualToAnchor:cardLabelVolume.leadingAnchor],
[cardNumLow.bottomAnchor constraintEqualToAnchor:_cardContainer.bottomAnchor constant:-10],
[cardNumLow.leadingAnchor constraintEqualToAnchor:cardLabelLow.leadingAnchor],
[cardNumChange.bottomAnchor constraintEqualToAnchor:cardNumLow.bottomAnchor],
[cardNumChange.leadingAnchor constraintEqualToAnchor:cardLabelChange.leadingAnchor],
[cardNumQuota.bottomAnchor constraintEqualToAnchor:cardNumLow.bottomAnchor],
[cardNumQuota.leadingAnchor constraintEqualToAnchor:cardLabelVolume.leadingAnchor]
]];
[NSLayoutConstraint constraintWithItem:cardLabelHeight
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:_cardContainer
attribute:NSLayoutAttributeTrailing
multiplier:0.25
constant:0].active = YES;
[NSLayoutConstraint constraintWithItem:cardLabelStart
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:_cardContainer
attribute:NSLayoutAttributeTrailing
multiplier:0.5
constant:0].active = YES;
[NSLayoutConstraint constraintWithItem:cardLabelVolume
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:_cardContainer
attribute:NSLayoutAttributeTrailing
multiplier:0.75
constant:0].active = YES;
// K线选择项容器
_kSelectContainer = [[UIView alloc] init];
_kSelectContainer.backgroundColor = [UIColor colorWithRed:26.0/255.0
green:26.0/255.0
blue:2.0/255.0
alpha:1.0];
_kSelectContainer.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:_kSelectContainer];
[self addKSelectOptions];
// K线图表容器
self.kLineContainer = [[UIView alloc] init];
self.kLineContainer.backgroundColor = [UIColor colorWithRed:26.0/255.0 green:26.0/255.0 blue:26.0/255.0 alpha:1.0];
self.kLineContainer.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:self.kLineContainer];
// K线图表滚动视图
self.kLineScrollView = [[UIScrollView alloc] init];
self.kLineScrollView.backgroundColor = [UIColor clearColor];
self.kLineScrollView.showsHorizontalScrollIndicator = NO;
self.kLineScrollView.userInteractionEnabled = YES;
self.kLineScrollView.translatesAutoresizingMaskIntoConstraints = NO;
[self.kLineContainer addSubview:self.kLineScrollView];
CGFloat priceLabelAreaWidth = 50.0;
// 滚动视图的约束
[NSLayoutConstraint activateConstraints:@[
[self.kLineScrollView.topAnchor constraintEqualToAnchor:self.kLineContainer.topAnchor],
[self.kLineScrollView.bottomAnchor constraintEqualToAnchor:self.kLineContainer.bottomAnchor],
[self.kLineScrollView.leadingAnchor constraintEqualToAnchor:self.kLineContainer.leadingAnchor constant:priceLabelAreaWidth],
[self.kLineScrollView.trailingAnchor constraintEqualToAnchor:self.kLineContainer.trailingAnchor],
[self.kLineScrollView.heightAnchor constraintEqualToAnchor:self.kLineContainer.heightAnchor]
]];
// 边框
UIView *borderView = [[UIView alloc] init];
borderView.backgroundColor = [UIColor clearColor];
borderView.layer.borderWidth = 1;
borderView.layer.borderColor = [[UIColor colorWithRed:0.4 green:0.4 blue:0.4 alpha:1.0] CGColor];
borderView.translatesAutoresizingMaskIntoConstraints = NO;
[self.kLineContainer addSubview:borderView];
[NSLayoutConstraint activateConstraints:@[
[borderView.leadingAnchor constraintEqualToAnchor:self.kLineContainer.leadingAnchor],
[borderView.trailingAnchor constraintEqualToAnchor:self.kLineContainer.trailingAnchor],
[borderView.topAnchor constraintEqualToAnchor:self.kLineContainer.topAnchor],
[borderView.bottomAnchor constraintEqualToAnchor:self.kLineContainer.bottomAnchor]
]];
// 动态计算模型数据的价格区间
CGFloat maxPrice = 0;
CGFloat minPrice = CGFLOAT_MAX;
for (id item in self.kLineData) {
if ([item isKindOfClass:[StockKLineModel class]]) {
StockKLineModel *model = (StockKLineModel *)item;
maxPrice = MAX(maxPrice, model.high);
minPrice = MIN(minPrice, model.low);
} else if ([item isKindOfClass:[NSDictionary class]]) {
NSDictionary *dict = (NSDictionary *)item;
maxPrice = MAX(maxPrice, [dict[@"high"] floatValue]);
minPrice = MIN(minPrice, [dict[@"low"] floatValue]);
}
}
maxPrice = maxPrice + (maxPrice - minPrice) * 0.1;
minPrice = minPrice - (maxPrice - minPrice) * 0.1;
// 价格刻度
NSMutableArray *priceLabelArr = [NSMutableArray array];
for (NSInteger i = 0; i < 5; i++) {
CGFloat price = maxPrice - (maxPrice - minPrice) / 4 * i;
UILabel *label = [[UILabel alloc] init];
label.text = [NSString stringWithFormat:@"%.1f", price];
label.textColor = [UIColor lightGrayColor];
label.font = [UIFont systemFontOfSize:10];
label.translatesAutoresizingMaskIntoConstraints = NO;
[self.kLineContainer addSubview:label];
[NSLayoutConstraint activateConstraints:@[
[label.leadingAnchor constraintEqualToAnchor:self.kLineContainer.leadingAnchor constant:5],
[label.centerYAnchor constraintEqualToAnchor:self.kLineContainer.centerYAnchor constant:-80 + 40 * i]
]];
[priceLabelArr addObject:label];
}
self.priceLabels = priceLabelArr;
self.dateLabels = @[];
}
- (void)addKSelectOptions {
NSArray *titles = @[@"日k", @"周k", @"月k", @"更多", @"设置"];
UIStackView *stackView = [[UIStackView alloc] init];
stackView.axis = UILayoutConstraintAxisHorizontal;
stackView.distribution = UIStackViewDistributionFillEqually;
stackView.alignment = UIStackViewAlignmentCenter;
stackView.spacing = 5.0;
stackView.translatesAutoresizingMaskIntoConstraints = NO;
for (NSString *title in titles) {
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
[button setTitle:title forState:UIControlStateNormal];
button.titleLabel.font = [UIFont systemFontOfSize:14];
[button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
if ([title isEqualToString:@"日k"]) {
[button setTitleColor:[UIColor yellowColor] forState:UIControlStateNormal];
button.layer.borderColor = [[UIColor yellowColor] CGColor];
button.layer.borderWidth = 1.0;
} else {
[button setTitleColor:[UIColor lightGrayColor] forState:UIControlStateNormal];
button.layer.borderWidth = 0;
}
button.layer.cornerRadius = 3.0;
button.clipsToBounds = YES;
[stackView addArrangedSubview:button];
}
[_kSelectContainer addSubview:stackView];
[NSLayoutConstraint activateConstraints:@[
[stackView.leadingAnchor constraintEqualToAnchor:_kSelectContainer.leadingAnchor constant:10],
[stackView.trailingAnchor constraintEqualToAnchor:_kSelectContainer.trailingAnchor constant:-10],
[stackView.topAnchor constraintEqualToAnchor:_kSelectContainer.topAnchor constant:5],
[stackView.bottomAnchor constraintEqualToAnchor:_kSelectContainer.bottomAnchor constant:-5]
]];
}
#pragma mark - Constraints
- (void)setupConstraints{
[NSLayoutConstraint activateConstraints:@[
[_cardContainer.topAnchor constraintEqualToAnchor:self.view.safeAreaLayoutGuide.topAnchor],
[_cardContainer.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[_cardContainer.widthAnchor constraintEqualToAnchor:self.view.widthAnchor],
[_cardContainer.heightAnchor constraintEqualToConstant:100],
[_kSelectContainer.topAnchor constraintEqualToAnchor:_cardContainer.bottomAnchor constant:5],
[_kSelectContainer.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[_kSelectContainer.widthAnchor constraintEqualToAnchor:self.view.widthAnchor],
[_kSelectContainer.heightAnchor constraintEqualToConstant:40],
[self.kLineContainer.topAnchor constraintEqualToAnchor:self.kSelectContainer.bottomAnchor constant:5],
[self.kLineContainer.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor],
[self.kLineContainer.widthAnchor constraintEqualToAnchor:self.view.widthAnchor],
[self.kLineContainer.heightAnchor constraintEqualToConstant:200]
]];
}
#pragma mark - KLine Drawing
- (void)drawKLineChart {
// 移除旧的 K 线
for (UIView *subview in self.kLineScrollView.subviews) {
if (![subview isKindOfClass:[UILabel class]]) {
[subview removeFromSuperview];
}
}
CGFloat chartHeight = self.kLineContainer.bounds.size.height;
if (chartHeight == 0) {
chartHeight = 200;
}
// K线宽度计算
CGFloat kLineUnitWidth = 5.0;
CGFloat kLineRatio = 0.8;
CGFloat kLineWidth = kLineUnitWidth * kLineRatio;
CGFloat space = kLineUnitWidth * (1.0 - kLineRatio);
CGFloat totalChartWidth = kLineUnitWidth * self.kLineData.count;
// 设置contentSize
self.kLineScrollView.contentSize = CGSizeMake(totalChartWidth, chartHeight);
// 动态计算价格区间
CGFloat maxPrice = 0;
CGFloat minPrice = CGFLOAT_MAX;
for (id item in self.kLineData) {
if ([item isKindOfClass:[StockKLineModel class]]) {
StockKLineModel *model = (StockKLineModel *)item;
maxPrice = MAX(maxPrice, model.high);
minPrice = MIN(minPrice, model.low);
} else if ([item isKindOfClass:[NSDictionary class]]) {
NSDictionary *dict = (NSDictionary *)item;
maxPrice = MAX(maxPrice, [dict[@"high"] floatValue]);
minPrice = MIN(minPrice, [dict[@"low"] floatValue]);
}
}
maxPrice = maxPrice + (maxPrice - minPrice) * 0.1;
minPrice = minPrice - (maxPrice - minPrice) * 0.1;
CGFloat priceRange = maxPrice - minPrice;
// 绘制K线
for (NSInteger i = 0; i < self.kLineData.count; i++) {
CGFloat open = 0, high = 0, low = 0, close = 0;
if ([self.kLineData[i] isKindOfClass:[StockKLineModel class]]) {
StockKLineModel *model = (StockKLineModel *)self.kLineData[i];
open = model.open;
high = model.high;
low = model.low;
close = model.close;
} else if ([self.kLineData[i] isKindOfClass:[NSDictionary class]]) {
NSDictionary *dict = (NSDictionary *)self.kLineData[i];
open = [dict[@"open"] floatValue];
high = [dict[@"high"] floatValue];
low = [dict[@"low"] floatValue];
close = [dict[@"close"] floatValue];
}
// 坐标转换
CGFloat yOpen = chartHeight * (maxPrice - open) / priceRange;
CGFloat yHigh = chartHeight * (maxPrice - high) / priceRange;
CGFloat yLow = chartHeight * (maxPrice - low) / priceRange;
CGFloat yClose = chartHeight * (maxPrice - close) / priceRange;
// 计算X坐标
CGFloat xPosition = space/2 + kLineUnitWidth * i;
// 绘制K线实体
UIView *bodyView = [[UIView alloc] init];
bodyView.backgroundColor = (close >= open) ? [UIColor greenColor] : [UIColor redColor];
bodyView.frame = CGRectMake(
xPosition,
MIN(yOpen, yClose),
kLineWidth,
MAX(1.0, fabs(yClose - yOpen))
);
[self.kLineScrollView addSubview:bodyView];
// 绘制影线
UIView *shadowView = [[UIView alloc] init];
shadowView.backgroundColor = bodyView.backgroundColor;
shadowView.frame = CGRectMake(
bodyView.center.x - 0.5,
yHigh,
1,
yLow - yHigh
);
[self.kLineScrollView addSubview:shadowView];
}
// 日期刻度
for (UILabel *label in self.dateLabels) {
[label removeFromSuperview];
}
NSMutableArray *dateLabelArr = [NSMutableArray array];
NSInteger dateInterval = 10;
for (NSInteger i = 0; i < self.kLineData.count; i += dateInterval) {
NSString *dateStr = @"";
if ([self.kLineData[i] isKindOfClass:[StockKLineModel class]]) {
StockKLineModel *model = (StockKLineModel *)self.kLineData[i];
dateStr = model.date;
} else if ([self.kLineData[i] isKindOfClass:[NSDictionary class]]) {
NSDictionary *dict = (NSDictionary *)self.kLineData[i];
dateStr = dict[@"date"];
}
UILabel *label = [[UILabel alloc] init];
label.text = dateStr;
label.textColor = [UIColor lightGrayColor];
label.font = [UIFont systemFontOfSize:10];
label.translatesAutoresizingMaskIntoConstraints = YES;
CGFloat xCenter = kLineUnitWidth * (i + 0.5);
[label sizeToFit];
label.frame = CGRectMake(
xCenter - label.bounds.size.width / 2,
chartHeight - 15,
label.bounds.size.width,
label.bounds.size.height
);
[self.kLineScrollView addSubview:label];
[dateLabelArr addObject:label];
}
self.dateLabels = dateLabelArr;
NSLog(@"ScrollView Bounds Width: %f", self.kLineScrollView.bounds.size.width);
NSLog(@"Content Size Width: %f", self.kLineScrollView.contentSize.width);
if (self.kLineScrollView.contentSize.width <= self.kLineScrollView.bounds.size.width) {
NSLog(@"⚠️ 警告:ContentSize 不够大,无法滑动。");
}
}
#pragma mark - Mock Data
- (void) setupData{
self.kLineData = @[
@{@"date":@"2025/11/18", @"open":@1620.0, @"high":@1643.0, @"low":@1605.0, @"close":@1630.0, @"volume":@2.5},
@{@"date":@"2025/11/19", @"open":@1630.0, @"high":@1635.0, @"low":@1615.0, @"close":@1620.0, @"volume":@2.2},
@{@"date":@"2025/11/20", @"open":@1620.0, @"high":@1628.0, @"low":@1608.0, @"close":@1615.0, @"volume":@1.8},
@{@"date":@"2025/11/21", @"open":@1615.0, @"high":@1625.0, @"low":@1610.0, @"close":@1622.0, @"volume":@2.0},
@{@"date":@"2025/11/22", @"open":@1622.0, @"high":@1630.0, @"low":@1618.0, @"close":@1625.0, @"volume":@2.3},
@{@"date":@"2025/11/25", @"open":@1625.0, @"high":@1632.0, @"low":@1620.0, @"close":@1628.0, @"volume":@1.9},
@{@"date":@"2025/11/26", @"open":@1628.0, @"high":@1635.0, @"low":@1622.0, @"close":@1630.0, @"volume":@2.1},
@{@"date":@"2025/11/27", @"open":@1630.0, @"high":@1638.0, @"low":@1625.0, @"close":@1628.0, @"volume":@2.4},
@{@"date":@"2025/11/28", @"open":@1628.0, @"high":@1632.0, @"low":@1609.0, @"close":@1610.0, @"volume":@2.6},
@{@"date":@"2025/11/29", @"open":@1610.0, @"high":@1620.0, @"low":@1608.0, @"close":@1615.0, @"volume":@1.7}
];
}
- (void)generateMockData {
NSMutableArray *arr = [NSMutableArray array];
CGFloat lastClose = 100.0;
NSLog(@"生成模拟数据中。。。");
// 生成200条数据(减少数量,避免contentSize过大)
for (int i = 0; i < 200; i++) {
StockKLineModel *model = [[StockKLineModel alloc] init];
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:-(200 - i) * 24 * 3600];
NSDateFormatter *fmt = [[NSDateFormatter alloc] init];
[fmt setDateFormat:@"MM-dd"];
model.date = [fmt stringFromDate:date];
// 价格生成逻辑
CGFloat volatility = lastClose * 0.02;
CGFloat randomChange = ((arc4random() % 100) / 100.0 - 0.5) * 2 * volatility;
model.open = lastClose + ((arc4random() % 100) / 100.0 - 0.5) * volatility * 0.5;
model.close = model.open + randomChange;
CGFloat maxOC = MAX(model.open, model.close);
CGFloat minOC = MIN(model.open, model.close);
model.high = maxOC + (arc4random() % 100) / 100.0 * 1.0;
model.low = minOC - (arc4random() % 100) / 100.0 * 1.0;
// 成交量生成逻辑
CGFloat baseVolume = 1000000.0;
CGFloat randomVolFactor = (arc4random() % 200) / 100.0 + 0.5;
if (fabs(model.close - model.open) / model.open > 0.03) {
randomVolFactor *= 1.5;
}
model.volume = baseVolume * randomVolFactor;
if (model.low < 0) model.low = 0.01;
[arr addObject:model];
lastClose = model.close;
}
NSLog(@"生成模拟数据完毕,共%ld条", arr.count);
self.kLineData = arr;
}
@end