基金净值估算程序 C#实现
By
jerrxjr1220
at 2023-07-24 • 0人收藏 • 969人看过
原来天天基金或者支付宝上都能直接看到当天的基金估值,从今天开始基金的当天估值全部下架了(真是把股民/基民当韭菜了,而且是套着麻袋割)
为了能实时看到基金的净值估值,只能自己通过基金公布的重仓股票份额自行估算涨跌。
于是就有了下面的代码,C#实现 Linqpad版,基金的持股份额保存在SQLite数据库中,其实也可以通过查询获取。
var jj = new JiJin("002910"); var stocks = Stocks.Select(x=>x); foreach (var item in stocks) { var change = GetStockChange(item.Stockname); jj.Stock[item.Stockname] = ((double)item.Percentage, change*0.01); } jj.Calculate(); jj.Dump(); $"{DateTime.Now.ToShortDateString()} 基金002910的净值估计变动{jj.TotalChange*100}%".Dump(); double GetStockChange(string code) { double change = 0; var wc = new WebClient(); wc.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183"); var str = wc.DownloadString("http://qt.gtimg.cn/q=" + code); var data = str.Split("~"); change = Convert.ToDouble(data[32].Trim()); return change; } class JiJin { public string Name { get; private set; } public Dictionary<string, (double, double)> Stock {get; set;} = new(); public double TotalChange {get; set;} public JiJin(string name) { Name = name; } public double Calculate() { double total=0; double totalpercent=0; double totalchange=0; double count=0; foreach (var item in Stock) { total += item.Value.Item1 * item.Value.Item2; totalpercent += item.Value.Item1; totalchange += item.Value.Item2; count++; } totalchange /= count; total += (1-totalpercent)*totalchange; TotalChange = total; return TotalChange; } }
13 个回复 | 最后更新于 2023-10-20
2023-07-24
#2
改进了一下,直接输入基金代码可以自动查询持仓并计算净值。
通过解析天天基金的网页进行计算。
var jj = new JiJin("012414"); jj.GetChiCang(); jj.Calculate(); $"{DateTime.Now.ToShortDateString()} 基金{jj.Name}的净值估计{jj.TotalChange*100}%".Dump(); class JiJin { public string Name { get; private set; } public Dictionary<string, (double, double)> Stock { get; set; } = new(); public double TotalChange { get; set; } public JiJin(string name) { Name = name; } public double Calculate() { double total = 0; double totalpercent = 0; double totalchange = 0; double count = 0; foreach (var item in Stock) { total += item.Value.Item1 * item.Value.Item2; totalpercent += item.Value.Item1; totalchange += item.Value.Item2; count++; } totalchange /= count; total += (1 - totalpercent) * totalchange; TotalChange = total; return TotalChange; } public void GetChiCang() { var wc = new WebClient(); wc.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183"); var str = wc.DownloadString($"http://fund.eastmoney.com/{Name}.html"); HtmlDocument doc = new HtmlDocument(); doc.LoadHtml(str); var node = doc.DocumentNode.SelectSingleNode("//*[@id='position_shares']/div[1]/table"); var content = node.InnerText; var c = content.Split(" ").Where(x => x.Length > 0); var list = c.ToArray()[4..]; for (var i=0; i<list.Length;i++) { if (list[i].EndsWith("%") && list[i+1].EndsWith("%")) { Stock[list[i-1]] = (Convert.ToDouble(list[i][..^1])*0.01, Convert.ToDouble(list[i+1][..^1])*0.01); } } } }
2023/7/24 星期一 基金012414的净值估计-1.1309255000000003%
对比实际当天净值,基本上还可以。
2023-07-26
#6
回复#5 @鸿湖重工业株式会社 :
完整工程文件下载:
aardio不是可以直接调用C#的类的吗?
import console; console.setTitle("MyJiJin"); import dotNet; var dll = dotNet.load("\res\JiJin.dll"); var JiJin = dll.import("JiJin"); var jj = JiJin.JiJin("002910"); jj.GetChiCang(); jj.Calculate(); console.log(jj.TotalChange); console.pause();
2023-07-27
#7
换了个异步写法,调用天天基金的api接口并解析json列表
var myjj = new JiJin("002910"); myjj.Adjustment = 0.9; myjj.GetStocks(); myjj.Calculate(); myjj.Dump(); public class JiJin { public string _Code { get; private set; } public string _Name { get; private set; } public double TotalChange {get; set;} public double Adjustment {get; set;} = 1; public List<Dictionary<string, (double, double)>> Stocks = new List<Dictionary<string, (double, double)>>(); public JiJin (string code) { _Code = code; } JToken GetChiCang() { var wc = new HttpClient(); wc.DefaultRequestHeaders.Add("User-Agent", "EMProjJijin/6.2.8 (iPhone; iOS 13.6; Scale/2.00)"); wc.DefaultRequestHeaders.Add("GTOKEN", "98B423068C1F4DEF9842F82ADF08C5db"); wc.DefaultRequestHeaders.Add("clientInfo", "ttjj-iPhone10,1-iOS-iOS13.6"); wc.DefaultRequestHeaders.Add("ContentType", "application/x-www-form-urlencoded"); wc.DefaultRequestHeaders.Add("Host", "fundmobapi.eastmoney.com"); wc.DefaultRequestHeaders.Add("Referer", "https://mpservice.com/516939c37bdb4ba2b1138c50cf69a2e1/release/pages/FundHistoryNetWorth"); var resp = wc.GetStringAsync($"https://fundmobapi.eastmoney.com/FundMNewApi/FundMNInverstPosition?FCODE={_Code}&OSVersion=14.3&appVersion=6.3.8&deviceid=3EA024C2-7F22-408B-95E4-383D38160FB3&plat=Iphone&product=EFund&serverVersion=6.3.6&version=6.3.8"); return JConstructor.Parse(resp.Result)["Datas"]["fundStocks"]; } async Task<double> GetStockChange(string code) { double change = 0; var wc = new HttpClient(); wc.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183"); var bytes = await wc.GetByteArrayAsync("http://qt.gtimg.cn/q=" + code); var str = Encoding.UTF8.GetString(bytes); var data = str.Split("~"); change = Convert.ToDouble(data[32].Trim()); return change; } public void GetStocks() { var chicang = GetChiCang(); foreach (var each in chicang) { var stock = ((string)each["GPDM"]).StartsWith("6") ? "sh"+(string)each["GPDM"] : "sz"+(string)each["GPDM"] ; var ccbl = (double)each["JZBL"]; var change = GetStockChange(stock).Result; Dictionary<string, (double, double)> s = new Dictionary<string, (double, double)>(); s[stock] = (ccbl, change); Stocks.Add(s); } } public double Calculate() { double total = 0; double totalpercent = 0; double totalchange = 0; int count = 0; foreach (var stock in Stocks) { KeyValuePair<string, (double, double)> pair = stock.FirstOrDefault(); total += pair.Value.Item1 * pair.Value.Item2 * 0.0001; totalpercent += pair.Value.Item1 * 0.01; totalchange += pair.Value.Item2 * 0.01; count++; } totalchange /= count; total += (1 - totalpercent) * totalchange; TotalChange = total * Adjustment; return TotalChange; } }
2023-07-28
#9
回复#8 @鸿湖重工业株式会社 :
这个还要看你调用的dll,有些比如申明了允许外部调用,有些可能是私有的,还有些可能是受保护的,或者是只读或者只写的属性。
例如,上面的这个类
public class JiJin { public string _Code { get; private set; } public string _Name { get; private set; } public double TotalChange {get; set;} public double Adjustment {get; set;} = 1; public List<Dictionary<string, (double, double)>> Stocks = new List<Dictionary<string, (double, double)>>(); public JiJin (string code) { _Code = code; } ...
这个类的“_Code”的写属性就是私有的,只能在创建时修改“_Code”的值,外部调用时只能是只读的,你要写就会报错。
2023-10-19
#10
<hc:GlowWindow x:Class="WPFFundation.Views.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:vm="clr-namespace:WPFFundation.ViewModels" xmlns:i="http://schemas.microsoft.com/xaml/behaviors" xmlns:hc="https://handyorg.github.io/handycontrol" d:DataContext="{d:DesignInstance Type=vm:MainViewModel}" mc:Ignorable="d" Title="MainWindow" Height="750" Width="600" FontFamily="JetBrains Mono" FontSize="{StaticResource TextFontSize}" Background="White"> <Window.Resources> </Window.Resources> <hc:SimplePanel> <ScrollViewer x:Name="scrollViewer" VerticalScrollBarVisibility="Auto"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="*" /> <RowDefinition Height="5*" /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="2*" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="4*" /> </Grid.ColumnDefinitions> <WrapPanel Grid.ColumnSpan="3" VerticalAlignment="Center"> <TextBlock Text="Fundation" Style="{StaticResource TextBlockDefaultDanger}" FontSize="20" Margin="20" /> <TextBox hc:InfoElement.Placeholder="Input Fundation Code ..." FontSize="20" Foreground="Brown" Style="{StaticResource TextBoxExtend}" Height="45" Width="300" Text="{Binding FundationCode, Mode=TwoWay}" /> <Button Style="{StaticResource ButtonDashedDanger}" Margin="20" Content="Search" FontSize="20" Height="40" Command="{Binding SearchCommand}" /> </WrapPanel> <StackPanel Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="3" HorizontalAlignment="Center"> <TextBox hc:InfoElement.Title="Adjustment" hc:InfoElement.TitlePlacement="Left" hc:InfoElement.Placeholder="1" hc:TitleElement.Foreground="{StaticResource PrimaryBrush}" Style="{StaticResource TextBoxExtend}" Width="400" Margin="0,10" Text="{Binding FundationAdjustment, Mode=TwoWay}" /> <TextBox hc:InfoElement.Title="Fundation Name" hc:InfoElement.TitlePlacement="Left" hc:TitleElement.Foreground="{StaticResource PrimaryBrush}" Style="{StaticResource TextBoxExtend}" Width="400" Margin="0,5" Text="{Binding FundationName}" /> <TextBox x:Name="estimate" hc:InfoElement.Title="Fundation Estimation" hc:InfoElement.TitlePlacement="Left" hc:TitleElement.Foreground="{StaticResource PrimaryBrush}" Style="{StaticResource TextBoxExtend}" Width="400" Margin="0,5" Text="{Binding FundationEstimation}" /> <i:Interaction.Triggers> <i:DataTrigger Binding="{Binding FundationColor}" Value="green"> <i:ChangePropertyAction TargetName="estimate" PropertyName="Foreground" Value="Green" /> </i:DataTrigger> <i:DataTrigger Binding="{Binding FundationColor}" Value="red"> <i:ChangePropertyAction TargetName="estimate" PropertyName="Foreground" Value="Red" /> </i:DataTrigger> </i:Interaction.Triggers> </StackPanel> <ScrollViewer Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="3"> <ListBox ItemsSource="{Binding Fundation.Stocks}"> <ListBox.ItemTemplate> <DataTemplate> <WrapPanel> <TextBlock x:Name="name" Text="{Binding StockName}" Margin="70,10,10,10" FontSize="20" Width="150" /> <TextBlock x:Name="price" Text="{Binding StockPrice}" Margin="10" FontSize="16" Width="80" /> <TextBlock x:Name="percent" Text="{Binding StockChangePercent, StringFormat={}{0} %}" Margin="10" FontSize="16" Width="80" /> <TextBlock x:Name="volume" Text="{Binding StockVolumePercentage, StringFormat={}{0} %}" Margin="10" FontSize="14" /> <i:Interaction.Triggers> <i:DataTrigger Binding="{Binding StockColor}" Value="green"> <i:ChangePropertyAction TargetName="percent" PropertyName="Foreground" Value="Green" /> </i:DataTrigger> <i:DataTrigger Binding="{Binding StockColor}" Value="red"> <i:ChangePropertyAction TargetName="percent" PropertyName="Foreground" Value="Red" /> </i:DataTrigger> </i:Interaction.Triggers> </WrapPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </ScrollViewer> </Grid> </ScrollViewer> </hc:SimplePanel> </hc:GlowWindow>
using System; using System.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel; namespace WPFFundation.Models; public partial class StockModel : ObservableObject { [ObservableProperty] private double _stockChangePercent; [ObservableProperty] private string _stockColor = "Grey"; [ObservableProperty] private string? _stockName; [ObservableProperty] private double _stockPrice; [ObservableProperty] private double _stockVolumePercentage; public StockModel() { StockName = string.Empty; } } public partial class FundationModel : ObservableObject { [ObservableProperty] private double _adjustment; [ObservableProperty] private double? _fundationChange; [ObservableProperty] private string _fundationName; [ObservableProperty] private double? _fundationValue; [ObservableProperty] private BindingList<StockModel> _stocks; [ObservableProperty] private DateTime _updateDateTime; public FundationModel() { Stocks = new BindingList<StockModel>(); } }
using System; using System.Net.Http; using System.Text; using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Newtonsoft.Json.Linq; using WPFFundation.Models; namespace WPFFundation.ViewModels; public partial class MainViewModel : ObservableObject { [ObservableProperty] private FundationModel _fundation; [ObservableProperty] private double _fundationAdjustment = 1; [ObservableProperty] private string? _fundationCode; [ObservableProperty] private string _fundationColor = "black"; [ObservableProperty] private string? _fundationEstimation; [ObservableProperty] private string? _fundationName; public MainViewModel() { Fundation = new FundationModel(); //发送消息 //WeakReferenceMessenger.Default.Send(new CustomizedMessage(new MessageModel())); } [RelayCommand] public async Task SearchAsync() { if(FundationCode.Length>=6) await GetStocksAsync(FundationCode); } public async Task<JToken> GetChiCangAsync(string fundationCode) { var wc = new HttpClient(); wc.DefaultRequestHeaders.Add("User-Agent", "EMProjJijin/6.2.8 (iPhone; iOS 13.6; Scale/2.00)"); wc.DefaultRequestHeaders.Add("GTOKEN", "98B423068C1F4DEF9842F82ADF08C5db"); wc.DefaultRequestHeaders.Add("clientInfo", "ttjj-iPhone10,1-iOS-iOS13.6"); wc.DefaultRequestHeaders.Add("ContentType", "application/x-www-form-urlencoded"); wc.DefaultRequestHeaders.Add("Host", "fundmobapi.eastmoney.com"); wc.DefaultRequestHeaders.Add("Referer", "https://mpservice.com/516939c37bdb4ba2b1138c50cf69a2e1/release/pages/FundHistoryNetWorth"); var resp = await wc.GetStringAsync( $"https://fundmobapi.eastmoney.com/FundMNewApi/FundMNInverstPosition?FCODE={fundationCode}&OSVersion=14.3&appVersion=6.3.8&deviceid=3EA024C2-7F22-408B-95E4-383D38160FB3&plat=Iphone&product=EFund&serverVersion=6.3.6&version=6.3.8"); return JObject.Parse(resp)["Datas"]!["fundStocks"]!; } public async Task<(double, double)> GetStockChangeAsync(string code) { double change; double price; var wc = new HttpClient(); wc.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36 Edg/115.0.1901.183"); var bytes = await wc.GetByteArrayAsync("http://qt.gtimg.cn/q=" + code); var str = Encoding.UTF8.GetString(bytes); var data = str.Split("~"); change = Convert.ToDouble(data[32].Trim()); price = Convert.ToDouble(data[3]); return (price, change); } public async Task GetStocksAsync(string fundationCode) { var chicang = await GetChiCangAsync(fundationCode); Fundation.Stocks.Clear(); foreach (var each in (JArray)chicang) { double stockPrice; double stockChangePercent; var stockCode = ((string)each["GPDM"]!).StartsWith("6") ? string.Format("sh{0:D6}", int.Parse((string)each["GPDM"]!)) : string.Format("sz{0:D6}", int.Parse((string)each["GPDM"]!)); var ccbl = (double)each["JZBL"]!; var stockName = each["GPJC"]!.ToString(); (stockPrice, stockChangePercent) = await GetStockChangeAsync(stockCode); var color = stockChangePercent > 0 ? "red" : "green"; Fundation.Stocks.Add(new StockModel { StockName = stockName, StockPrice = stockPrice, StockChangePercent = stockChangePercent, StockVolumePercentage = ccbl, StockColor = color }); Calculate(); } } public void Calculate() { double sum = 0; double restPercentage = 100; double averageChangePercent = 0; foreach (var stock in Fundation.Stocks) { sum += stock.StockChangePercent * stock.StockVolumePercentage * 0.01; restPercentage -= stock.StockVolumePercentage; averageChangePercent += stock.StockChangePercent; } averageChangePercent /= Fundation.Stocks.Count; sum += restPercentage * averageChangePercent * 0.01; FundationEstimation = $"{sum * FundationAdjustment:F2} %"; FundationColor = sum > 0 ? "red" : "green"; } } //定义消息 // public class CustomizedMessage : ValueChangedMessage<MessageModel> // PropertyChangedMessage // RequestMessage ( Async + ..) // { // public CustomizedMessage(MessageModel value) : base(value) // { // } // }
using HandyControl.Controls; using WPFFundation.ViewModels; namespace WPFFundation.Views; /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : GlowWindow { private readonly MainViewModel mainViewModel; public MainWindow() { InitializeComponent(); #region 注册并接受消息 //WeakReferenceMessenger.Default.Register<CustomizedMessage>(this, (o, m) => { MessageBox.Show("Received!"); }); #endregion mainViewModel = new MainViewModel(); DataContext = mainViewModel; } }
登录后方可回帖
估计的净值,难免是有误差,但是总好过被套着麻袋割韭菜