这是到现在为止,第一个用C#完整做出来的金融应用,虽然还有许多需要完善的地方,但是框架已经搭建好了。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Data; namespace VIX { public enum OptionType { Call, Put } static class Read { //从CSV读取所有数据到DataTable,认为CSV文件中第一行为标题 public static DataTable CSV(string path, ListtypeList, bool hasHead = true) { string line = ""; DataTable dt = new DataTable(); FileStream fs = new FileStream(path, System.IO.FileMode.Open, System.IO.FileAccess.Read); StreamReader sr = new StreamReader(fs, System.Text.Encoding.Default); //写入列名称 if (hasHead) { line = sr.ReadLine(); string[] columnNames = line.Split(','); for (int i = 0; i < columnNames.Length; i++) { dt.Columns.Add(columnNames[i],typeList[i]); //添加列明以及类型 } } //开始读取数据 line = sr.ReadLine(); while (line != null) { dt.Rows.Add(line.Split(',')); //将内容分割成数组,并且写入行中 line = sr.ReadLine(); } sr.Close(); fs.Close(); return dt; } } static class TypeList { //格式化Info Table public static List GetInfoTypeList() { return new List (new Type[] { typeof(int), typeof(string), typeof(double), typeof(string), typeof(int) }); } //格式化Trading Data的typeList public static List GetTrdTypeList(int numOptions) { List trdTypeList = new List (100); for (int i = 0; i < 2 + 2 * numOptions; i++) { if (i == 0) trdTypeList.Add(typeof(DateTime)); //第一列为时间 else trdTypeList.Add(typeof(Double)); //第二列是ETF;后面的列都是Bid和Ask } return trdTypeList; } } static class Filter { //根据给定的 near / next (int) 类型,将 InfoDT 中的对应的符合条件的行筛选出来,组合成数据表(结果按照行权价格排列) static public DataTable NearOrNextInfoDT(DataTable infoDT, int intNearNext) { return infoDT.AsEnumerable().Select(dr => dr).Where(dr => dr.Field ("行权日") == intNearNext).OrderBy(dr => dr.Field ("行权价")).CopyToDataTable(); } //给定 Forward Price,找出以它为标准的 out-of-money call 或者 put codes -> Strike //Key = code, Value = double [2] { strike, midquote} static public void GetDictCodesStrikeMidquote(ref Dictionary ResultDict, DataTable NearOrNextInfoTB, DataRow TrdDR,double K0, OptionType OpType) { #region 筛选出执行价格高于或者低于K0的call或者put if (OpType==OptionType.Call) { for (int i = 0; i < ResultDict.Count; i++) { if (ResultDict.ElementAt(i).Value[0] <= K0) { ResultDict.Remove(ResultDict.ElementAt(i).Key); i--; ResultDict.OrderByDescending(kvp => kvp.Value[0]); //按照行权价格降序排序 } } //dictCodeStrike = NearOrNextInfoTB.AsEnumerable().Where(dr => dr.Field ("行权价") > K0 && dr.Field ("期权类型") == "认购") // .OrderByDescending(dr => dr.Field ("行权价")).Select(dr => dr) // .ToDictionary(dr => dr.Field ("期权代码"), dr =>new double[] {dr.Field ("行权价"),0}); // Key, Strike, MidQuote = 0 } else if (OpType == OptionType.Put) { for (int i = 0; i < ResultDict.Count; i++) { if (ResultDict.ElementAt(i).Value[0] >= K0) { ResultDict.Remove(ResultDict.ElementAt(i).Key); i--; ResultDict.OrderBy(kvp => kvp.Value[0]); //按照行权价格升序排列 } } //dictCodeStrike = NearOrNextInfoTB.AsEnumerable().Where(dr => dr.Field ("行权价") < K0 && dr.Field ("期权类型") == "认沽") // .OrderBy(dr => dr.Field ("行权价")).Select(dr => dr) // .ToDictionary(dr => dr.Field ("期权代码"), dr => new double[] { dr.Field ("行权价"), 0 }); } #endregion Dictionary tempDict = new Dictionary (); bool isLastBidZero = false; //上一个Bid是不是0? for (int i = ResultDict.Count - 1; i >= 0; i--) { if ((double)TrdDR[ResultDict.ElementAt(i).Key + "买一价"] == 0 && isLastBidZero == true) { //相邻连续的两个put的bid都是0 break; } else if ((double)TrdDR[ResultDict.ElementAt(i).Key + "买一价"] == 0 && isLastBidZero == false) { //相邻第一次出现 0 isLastBidZero = true; continue; } else { //符合条件的加入到tempDict tempDict.Add(ResultDict.ElementAt(i).Key,ResultDict.ElementAt(i).Value); isLastBidZero = false; } } ResultDict = tempDict; } //根据Forward Price返回K0 static public double GetK0(ref DataTable InfoTB, double F) { return InfoTB.AsEnumerable().Select(dr => dr.Field ("行权价")).Where(K => K < F).Max(); } //返回最终需要处理的数据 static public Dictionary GetFinalData(DataTable NearOrNextInfoTB, double K0, DataRow TrdDataRow, Dictionary Call_DictCodeStrikeMidquote, Dictionary Put_DictCodeStrikeMidquote) { //Add option with strike = K0 string[] CallandPutAtK0 = NearOrNextInfoTB.AsEnumerable().Where(dr => dr.Field ("行权价") == K0).Select(dr => dr.Field ("期权代码")).ToArray(); double callMid = Calculate.MidQuote(TrdDataRow, CallandPutAtK0[0]); //trdDT.Rows[trdRow] double putMid = Calculate.MidQuote(TrdDataRow, CallandPutAtK0[1]); double OpAtK0 = 0.5 * (callMid + putMid); //Combine Two Dict!!! Dictionary dict = new Dictionary (); for (int i = 0; i < Call_DictCodeStrikeMidquote.Count; i++) { dict.Add(Call_DictCodeStrikeMidquote.ElementAt(i).Key, Call_DictCodeStrikeMidquote.ElementAt(i).Value); } dict.Add("OPATM", new double[] { K0, OpAtK0 }); for (int i = 0; i < Put_DictCodeStrikeMidquote.Count; i++) { dict.Add(Put_DictCodeStrikeMidquote.ElementAt(i).Key, Put_DictCodeStrikeMidquote.ElementAt(i).Value); } return dict; } } static class Calculate { //Get the mid quote for the option wanted in Trade Table row option static public double MidQuote(DataRow TrdDataRow, string OptionCode) { return 0.5 * ((double)TrdDataRow[OptionCode + "卖一价"] + (double)TrdDataRow[OptionCode + "买一价"]); } //Calculate T1 or T2 static public double T(DateTime CurrentDate, int intNearNext, double MINSINYEAR) { DateTime date = DateTime.ParseExact(intNearNext.ToString() + @" 15:00:00", "yyyyMMdd HH:mm:ss", null); TimeSpan ts = date - CurrentDate; return ts.TotalMinutes / MINSINYEAR; } //Get temp K, which is used to calculate F, via finding out the smallest difference between call and put static public void TempStrike(ref double tempK, ref double minDiff, double callMidQuote, double putMidQuote, int InfoRowNO, DataTable InfoTB) { double tempDiff = callMidQuote - putMidQuote; double absDiff = Math.Abs(tempDiff); //Find the lowest difference of | Call - Put | if (InfoRowNO == 0) { minDiff = tempDiff; tempK = (double)InfoTB.Rows[InfoRowNO]["行权价"]; } else { if (Math.Abs(minDiff) > absDiff) { minDiff = tempDiff; tempK = (double)InfoTB.Rows[InfoRowNO]["行权价"]; } } } //Return Sigma^2 static public double SigmaSquare(Dictionary DictCodeStrikeMidquote,double T,double R,double F,double K0) { double contributionSum = 0; int numPairs = DictCodeStrikeMidquote.Count; double deltaK; //将Dict按照执行价格排序 KeyValuePair [] dictArr = DictCodeStrikeMidquote.OrderBy(kvp => kvp.Value[0]).ToArray(); if (numPairs <=1) {//如果数量少于或者等于一个,那么就直接返回0(可以作为异常) return 0; } else { for (int i = 0; i < numPairs; i++) { //位于顶端和尾端的情况 if (i == 0) deltaK = dictArr.ElementAt(i + 1).Value[0] - dictArr.ElementAt(i).Value[0]; else if (i == numPairs - 1) deltaK = dictArr.ElementAt(i).Value[0] - dictArr.ElementAt(i - 1).Value[0]; else //中间的情况 deltaK = 0.5 * (dictArr.ElementAt(i + 1).Value[0] - dictArr.ElementAt(i - 1).Value[0]); //累加各个期权的contribution contributionSum += Contribution(deltaK, dictArr.ElementAt(i).Value[0], R, T, dictArr.ElementAt(i).Value[1]); } return 2 / T * contributionSum - 1 / T * Math.Pow(F / K0 - 1,2) ; } } //Return contribution of each option in the target range static public double Contribution(double DeltaK, double K, double R, double T, double MidQuote) { return DeltaK / (K * K) * Math.Exp(R * T) * MidQuote; } Return the KeyValuePair List ( key is STRIKE, value is MID QUOTE ) for the calculation of sigma square //static public List > DictListOFStrikeAndQuote(double K0, DataTable NearOrNextInfoTB, DataRow TrdDataRow, // Dictionary dictPutStrikesAndMidQuote, Dictionary dictCallStikesAndMidQuote) //{ // //Select the strike price immediate below F1 for near // Dictionary nearFiltedPuts = Filter.GetDictCodesStrike(NearOrNextInfoTB, TrdDataRow, K0, OptionType.Put); //trdDT.Rows[trdRow] // Dictionary nearFiltedCalls = Filter.GetDictCodesStrike(NearOrNextInfoTB, TrdDataRow, K0, OptionType.Call); // //Add option with strike = K0 // string[] CallandPutAtK0 = NearOrNextInfoTB.AsEnumerable().Where(dr => dr.Field ("行权价") == K0).Select(dr => dr.Field ("期权代码")).ToArray(); // double callMid = Calculate.MidQuote(TrdDataRow, CallandPutAtK0[0]); //((double)trdDT.Rows[trdRow][CallandPutAtK0[0] + "卖一价"] + (double)trdDT.Rows[trdRow][CallandPutAtK0[0] + "买一价"]) / 2; // double putMid = Calculate.MidQuote(TrdDataRow, CallandPutAtK0[1]); //((double)trdDT.Rows[trdRow][CallandPutAtK0[1] + "卖一价"] + (double)trdDT.Rows[trdRow][CallandPutAtK0[1] + "买一价"]) / 2; // double OpAtK0 = 0.5 * (callMid + putMid); // var q1 = from r1 in dictPutStrikesAndMidQuote // join r2 in nearFiltedPuts // on r1.Key equals r2.Value // select r1; // var q2 = from r1 in dictCallStikesAndMidQuote // join r2 in nearFiltedCalls // on r1.Key equals r2.Value // select r1; // List > dict = q1.ToList(); // dict.Add(new KeyValuePair (K0, OpAtK0)); // dict.AddRange(q2.ToList()); // dict.OrderBy(kvp => kvp.Key); // return dict; //} } static class Loop { //the loop in near or next info table //使用了Out关键字来输出Call和Put的由 Code, Strike, MidQuote 组成的字典 public static void inNearOrNextTB(DataTable NearOrNextInfoTB, DataRow TrdDataRow,out double Ktemp, out double minDiff, out Dictionary Call_DictCodeStrikeMidquote, out Dictionary Put_DictCodeStrikeMidquote) { //out 必须要初始化 Call_DictCodeStrikeMidquote = new Dictionary (); Put_DictCodeStrikeMidquote = new Dictionary (); Ktemp = 0; minDiff = 0; for (int nearInfoRow = 0; nearInfoRow < NearOrNextInfoTB.Rows.Count - 1; nearInfoRow += 2) { //对于Info Table里面的每个 Option //1、得到它的代码 Code string callCode = NearOrNextInfoTB.Rows[nearInfoRow]["期权代码"].ToString(); string putCode = NearOrNextInfoTB.Rows[nearInfoRow + 1]["期权代码"].ToString(); //2、得到 Code对应的中点Quote double callMidQuote = Calculate.MidQuote(TrdDataRow, callCode); //trdDT.Rows[trdRow] double putMidQuote = Calculate.MidQuote(TrdDataRow, putCode); //trdDT.Rows[trdRow] //添加到字典中,为最终求值做准备 Call_DictCodeStrikeMidquote.Add((string)NearOrNextInfoTB.Rows[nearInfoRow]["期权代码"], new double[] {(double)NearOrNextInfoTB.Rows[nearInfoRow]["行权价"], callMidQuote}); Put_DictCodeStrikeMidquote.Add((string)NearOrNextInfoTB.Rows[nearInfoRow+1]["期权代码"], new double[] {(double)NearOrNextInfoTB.Rows[nearInfoRow+1]["行权价"], putMidQuote}); //得到 Temp Strike Price Calculate.TempStrike(ref Ktemp, ref minDiff, callMidQuote, putMidQuote, nearInfoRow, NearOrNextInfoTB); } } } using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using System.Diagnostics; namespace VIX { class Program { //如果当月合约到期日小于LIMIT,那么near合约将由原来的next来替换, //而原来的next将由未来最近第二期的合约替换 public const int LIMIT = 0; public const double R = 0.1; //无风险利率 public const double N365 = 365 * 24 * 60; public const int N30 = 30 * 24 * 60; public static int current = 20151029; public static int near; public static int next; public static DateTime currentDate; public static DateTime nearDate; public static DateTime nextDate; public static double T1; public static double T2; public static double NT1; public static double NT2; public static double F1; //Forward Price public static double F2; public static DataTable infoDT; public static DataTable trdDT; public static ListtypeListInfo; //infoDT表格的列属性 public static List typeListTrd; //trdDT表格的列属性 public static double K01; public static double K02; public static double Ktemp1; public static double Ktemp2; public static double minDiff1; public static double minDiff2; static void Main(string[] args) { //传入期权信息表 typeListInfo=TypeList.GetInfoTypeList(); infoDT = Read.CSV(@"F:\Internship_Projects\VIX\Data\OptionInfo_" + current + @".CSV", typeListInfo); //得到期权数量 int numOption = infoDT.Rows.Count; //得到四个经过排序的执行日期 int[] execDates = infoDT.AsEnumerable().Select(dr => dr.Field ("行权日")).Distinct().OrderBy(a => a).ToArray(); //得到near和next的执行日期 NearNextExecDate(current, execDates, ref near, ref next); //得到near和next对应的info table,并且是按照 执行价格 排序 DataTable nearInfoTB=Filter.NearOrNextInfoDT(infoDT,near); DataTable nextInfoTB=Filter.NearOrNextInfoDT(infoDT,next); //根据numOption可以知道trdDT里面有多少列 typeListTrd = TypeList.GetTrdTypeList(numOption); trdDT = Read.CSV(@"F:\Internship_Projects\VIX\Data\TradingData_" + current + @".CSV", typeListTrd); / //大循环 for (int trdRow = 0; trdRow < trdDT.Rows.Count -4 ; trdRow++) //循环至14:56 { //Determine T1 and T2 currentDate = (DateTime)trdDT.Rows[trdRow][0]; T1 = Calculate.T(currentDate, near, N365); T2 = Calculate.T(currentDate, next, N365); NT1 = T1 * N365; NT2 = T2 * N365; Dictionary Call_DictCodeStrikeMidquote01; Dictionary Put_DictCodeStrikeMidquote01; Dictionary Call_DictCodeStrikeMidquote02; Dictionary Put_DictCodeStrikeMidquote02; //Determine the K0 //near:得到所有near时间到期的Call和Put Loop.inNearOrNextTB(nearInfoTB, trdDT.Rows[trdRow], out Ktemp1, out minDiff1, out Call_DictCodeStrikeMidquote01, out Put_DictCodeStrikeMidquote01); Loop.inNearOrNextTB(nextInfoTB, trdDT.Rows[trdRow], out Ktemp2, out minDiff2, out Call_DictCodeStrikeMidquote02, out Put_DictCodeStrikeMidquote02); //calculate the near forward price F1 = Ktemp1+ Math.Exp(R * T1) * minDiff1; F2 = Ktemp2 + Math.Exp(R * T2) * minDiff2; //Get the real K0 according to F K01 = Filter.GetK0(ref infoDT, F1); K02 = Filter.GetK0(ref infoDT, F2); //Select the strike price immediate below F1 for near Filter.GetDictCodesStrikeMidquote(ref Put_DictCodeStrikeMidquote01, nearInfoTB, trdDT.Rows[trdRow], K01, OptionType.Put); Filter.GetDictCodesStrikeMidquote(ref Call_DictCodeStrikeMidquote01, nearInfoTB, trdDT.Rows[trdRow], K01, OptionType.Call); Filter.GetDictCodesStrikeMidquote(ref Put_DictCodeStrikeMidquote02, nextInfoTB, trdDT.Rows[trdRow], K02, OptionType.Put); Filter.GetDictCodesStrikeMidquote(ref Call_DictCodeStrikeMidquote02, nextInfoTB, trdDT.Rows[trdRow], K02, OptionType.Call); Dictionary dict1 = Filter.GetFinalData(nearInfoTB, K01, trdDT.Rows[trdRow], Call_DictCodeStrikeMidquote01, Put_DictCodeStrikeMidquote01); Dictionary dict2 = Filter.GetFinalData(nextInfoTB, K02, trdDT.Rows[trdRow], Call_DictCodeStrikeMidquote02, Put_DictCodeStrikeMidquote02); double SigmaSquare1 = Calculate.SigmaSquare(dict1,T1,R,F1, K01); double SigmaSquare2 = Calculate.SigmaSquare(dict2, T2, R, F2, K02); double tmp = T1*SigmaSquare1*(NT2-N30)/(NT2-NT1)+T2*SigmaSquare2*(N30-NT1)/(NT2-NT1); double Vix = 100 * Math.Sqrt(N365 / N30 * tmp); Debug.WriteLine(Vix); }//大循环 } //给定currentDate,得到near和next static void NearNextExecDate(int current,int[] sortedExecDates, ref int near, ref int next) { //如果currentDate离第一个执行日期的距离在LIMIT之外 if (sortedExecDates[0] - current >= LIMIT) { near = sortedExecDates[0]; next = sortedExecDates[1]; return; } else { near = sortedExecDates[1]; next = sortedExecDates[2]; return; } } } }
}