引子
为了在上班的过程中顺利关心工资未来增值的可能性,今天小编就来做一个在Grasshopper里看基金净值的小电池,大致就是实现一个“给定基金代码,通过网络API调用获取基金净值信息,并输出该基金的所有净值信息”。要实现这个功能,首先我们需要首先进行需求分析,然后确定实现的逻辑,最后进行代码编写和简单的测试。
本文主要内容有…
需求分析
电池内部逻辑实现
– 网络请求发送
– 返回Json字符串解析
– 数据传出
测试及总结
需求分析
是
“给定基金代码”
“输出该基金的所有净值”
“给定基金代码”
一般而言,基金代码都是整型数值,但是考虑到日常使用的时候,基金代码前可能会包含前置位的数字0,比如“002638”——这种带前置0的方式如果用整型来处理就会麻烦一些,故认为它会是整型或者是字符串。
不过,由于在 Grasshopper 里,字符串类型(GH_String)和整数类型(GH_Integer)会自动进行隐式转换,其实不需要担心到底会是整形还是字符串,只需确定我们代码最终接受的类型,GH会在输入参数中自动帮我们进行类型转换。
“输出该基金的所有净值信息”
基金净值是浮点型,但是由于需要获取所有净值信息,包含历史上基金存在时所有的值,所以对应每一个基金代码应该会附带一个列表的信息。
净值是浮点型。其实理论上来说,对于任何与金额相关的数据类型都应该使用 decimal 类型,否则就会出现精度误差,一个经典的例子就是 double 数据类型无法精确表示小数0.3,但是由于在这里我们的 Grasshopper 内建数据类型中没有 decimal,即便此处设置为 decimal,在输出时也会强行给转成 double,所以干脆就双精度浮点型 double 得了。
经过上述分析:
1. 电池的输入端需要一个参数,可以是整型或字符串,这个项目我们就直接采用整型;
2. 输出端需要一个参数,是浮点型;
3. 输入和输出直接的对应关系是一个整型输入对应一个浮点型列表,确定输入端的参数的Access属性为item,输出端的参数的Access属性为list。
此时可以开一个Visual Studio,新建一个Grasshopper电池项目,然后将我们的输入、输出端的参数相关代码放入了。
protectedoverridevoidRegisterInputParams
(GH_InputParamManagerpManager)
{
pManager.AddIntegerParameter(“Code”,
“C”,”基金代码”,GH_ParamAccess.item);
}
protectedoverridevoidRegisterOutputParams
(GH_OutputParamManagerpManager)
{
pManager.AddNumber(“DayValues”,
“v”,”净值”,GH_ParamAccess.list);
}
电池内部逻辑实现
这部分是整个电池项目中最耗时的部分,也就是在电池类的`SolveInstance`中处理输入的基金代码信息,并给出最终的净值浮点数列表。
首先获取基金的代码:
protectedoverridevoidSolveInstance(IGH_DataAccessDA)
{
//获取基金代码,检测获取是否成功,若不成功直接退出电池执行
intcode=int.MinValue;
boolSuccess=DA.GetData(0,refcode);
if(!Success)return;
}
然后就是对基金净值API的调用,这里调用的是 “小熊同学”网站的基金API。
(https://www.doctorxiong.club/#/guide)
这个API的返回值是一个json字符串,里面包含基金的名称、代码、净值等信息,详细的API文档可在网站中找到。
现在的问题就聚焦到两个部分了,一个是如何发送网络请求,另一个是如何在返回的json字符串中提取我们需要的信息。
网络请求的发送
由于我们是在 Grasshopper 中实现该逻辑,所以运行环境是 .NETFramework4.5。在网络上找资料时也需要时刻注意运行环境。目前.NET生态已经布局到全平台,很多相关网络请求的资料找到的都是 .NETCore 或者 .NET 的,而 .NETFramework 与它们略有不同,依赖项可能会不一样。
在.NETFramework框架下,网络请求的发送可以使用System.Net 命名空间里的 WebRequest 类实现。
(具体.NET/.NETCore/.NETFramework三者有什么区别和联系读者们也可以自行探索一下,相关历史介绍还是挺多的)
protectedoverridevoidSolveInstance(IGH_DataAccessDA)
{
//获取基金代码,检测获取是否成功,若不成功直接退出电池执行
//……….略
//建立请求
varreq=WebRequest.Create(@”https://api.doctorxiong.club/v1/fund/detail?code=”+code.ToString());
req.Method=”GET”;
req.Headers.Add(HttpRequestHeader.AcceptCharset,”utf-8″);
//发起请求,将请求返回保存至res中
varres=req.GetResponse();
}
解析Json字符串
接下来,
就需要对返回的json字符串进行解析了。考虑到现在网络编程的便利性,json字符串几乎已经成为网络编程中数据往来的标准格式,所以json字符串的解析就不需要手写了,.NET框架已经自带了json字符串解析的功能,可以自动将json字符串封装成一个 C#的object提供原生访问,十分方便。
但是,在.NET下很方便使用的json解析功能在 .NETFramework 中是不能使用的!所以我们需要用到它的前生Newtonsoft.Json。这个DLL需要手动添加,具体添加方法为:
1.在VisualStudio的项目管理器中找到引用(References)并右键单击,选择添加引用
2.弹出窗口左上角选择Assemblies->Extensions
3.找到并选择Json.NET,单击确定添加,此时引用中出现 Newtonsoft.Json
引用添加成功后我们可以在代码中加入命名空间
usingNewtonsoft.Json;
此时我们离解析成功仅差一步之遥了。
前面提到用使用Newtonsoft.Json可以自动把json字符串转换为一个可以操作的对象实例,其前提是,这个对象实例我们需要有这个对象的数据模型。
想象json是一堆货物,Newtonsoft.Json可以帮忙装货,但是它需要知道每种货物需要被装在什么位置,我们需要指明数据会被装在什么对象的什么属性中。
数据模型其实就是一个类,我们需要依据json的格式去构建这个类的属性,下面就直接给出笔者建立的数据模型。笔者在这里使用的是 struct 结构体,读者也可以自行换成 class 关键字。
publicstructResponseData
{
publicHttpStatusCodeCode{get;set;}
publicstringMessage{get;set;}
publicFundData{get;set;}
publicstringMeta{get;set;}
publicstructFund
{
publicstringCode{get;set;}
publicstringName{get;set;}
publicstringType{get;set;}
publicfloatNetWorth{get;set;}
publicfloatExpectWorth{get;set;}
publicfloatTotalWorth{get;set;}
publicstringExpectGrowth{get;set;}
publicstringDayGrowth{get;set;}
publicstringLastWeekGrowth{get;set;}
publicstringLastMonthGrowth{get;set;}
publicstringLastThreeMonthsGrowth{get;set;}
publicstringLastSixMonthsGrowth{get;set;}
publicstringLastYearGrowth{get;set;}
publicstringBuyMin{get;set;}
publicstringBuySourceRate{get;set;}
publicstringBuyRate{get;set;}
publicstringManager{get;set;}
publicstringFundScale{get;set;}
publicstringNetWorthDate{get;set;}
publicstringExpectWorthDate{get;set;}
publicstring[][]NetWorthData{get;set;}
publicstring[][]TotalNetWorthData{get;set;}
}
}
有了这个数据模型,我们就可以直接对返回的json字符串进行解析,代码如下:
MemoryStream类需要用到命名空间System.IO
Encoding类需要用命名空间System.Text
protectedoverridevoidSolveInstance(IGH_DataAccessDA)
{
//获取基金代码,检测获取是否成功,若不成功直接退出电池执行
//建立请求
//发起请求,将请求返回保存至res中
//准备解析
stringjsonString;
//获取返回流
using(varresponseStream=res.GetResponseStream())
{
//在内存中创建临时存储用来存储返回流数据
using(varms=newMemoryStream())
{
//返回流数据复制至内存中
responseStream.CopyTo(ms);
//用UTF8解码返回流数据
jsonString=Encoding.UTF8.GetString(ms.ToArray());
//解析json字符串
varresData=JsonConvert.DeserializeObject<ResponseData>(jsonString);
}
}
}
最终我们获得了变量 resData 就是最终我们解析得到的数据,它是一个 ResponseData 结构体的实例,我们可以在后续传出数据时方便地通过该结构体获得数据。
数据传出
下面的事情
就又变得简单了起来。
由于我们的输出是对应一个数据列表,我们仅需从 resData 中提取到我们想要的基金净值数据,存储在一个 List<double> 中,然后借由 DA.SetDataList() 方法传出即可。
通过基金API可知,基金净值信息包含在 NetWorthData 中,每个NetWorthData包含4个数据,分别是“日期、净值、日涨跌、额外信息”。我们所需要的净值在该列表的第二项。
又因为所有的数据在数据模型中是以字符串形式给出,我们需要使用double.Parse来进行格式转换……等等……GH可以自动隐式转换,我们不如直接输出字符串试试看?
protectedoverridevoidSolveInstance(IGH_DataAccessDA)
{
//获取基金代码,检测获取是否成功,若不成功直接退出电池执行
//建立请求
//发起请求,将请求返回保存至res中
//准备解析
stringjsonString;
//获取返回流
using(varresponseStream=res.GetResponseStream())
{
//在内存中创建临时存储用来存储返回流数据
using(varms=newMemoryStream())
{
//返回流数据复制至内存中
//用UTF8解码返回流数据
//解析json字符串
//提取每天的净值信息,放入列表中
List<string>dayValues=newList<string>();
foreach(variteminresData.Data.NetWorthData)
dayValues.Add(item[1]);
//传出数据
DA.SetDataList(0,dayValues);
}
}
}
这样我们的电池就完工了!下面赶紧让我们来试试看把。文章最后有完整cs文件的代码可供参考
测试及总结
首先要做的是在Rhino中使用 GrasshopperDevelopSetting 命令加入我们电池的输出目录。具体细节可以参考我们公众号的Grasshopper基础教程,本文在此不再复述。设置完成后关闭Rhino。
点击运行,让我们测试一下自己的电池吧!
当然,硬是要输入一个不存在的基金代码,此时我们无法成功地解析服务器返回的json字符串,GH也能帮我们完成错误处理……
通过这个小电池,我们从如何收集数据、实现自己的逻辑,到最后传出数据,进行简单的测试。涉及到的点大致有下面几个内容
1.输入/输出参数的确定,以及它们的类型,相关的GH特有的隐式类型转换
2.对 WebAPI 发起一个网络请求
3.对返回的json字符串进行解析
4.简单的测试
其中,“json字符串解析 ”部分本文并没有详细展开,因为它本身就能做一个超级大的内容了,这里只给出了一个具体的应用。想要详细了解的读者可以自行搜索更多于json字符串解析的内容。
另外,“网络请求”相关的内容,读者在搜索额外资料时请注意适用环境,.NET/.NETCore 是一类环境,.NETFramework 与它们是有一些差别的。
最后,部分代码涉及到了 “流(Stream)”的概念,这是有关于I/O相关的内容。初步理解的话就是可以认为“流”是一系列函数,最后的数据会在所有的中间的流中过一遍,也就是实现了对整个数据流的依次处理。读者感兴趣的话可以自行继续深入学习。
电池代码部分详见
https://gitee.com/fodan/non-structure_-code_-share/blob/master/Grasshopper_GetFundDetails.cs
我们非解构一直关注建筑艺术与结构技术的有机融合。我们在做好设计的同时,一直关注数字化、智能化等前沿技术在建筑设计行业中的运用,这些年一直在坚持探索和实践。
非常欢迎优秀的你来加入我们,一起来跨界,做一名推动行业发展的斜杠青年。
这几年,对参数化设计感兴趣的朋友越来越多,我们的参数化设计交流群也已经发展到了5群,欢迎更多的朋友加入,相互交流学习。
添加我们“转自:非解构-公众号”微信,
加入参数化设计交流群。
不了解我们的可以来补课了