Overall Statistics |
Total Orders 0 Average Win 0% Average Loss 0% Compounding Annual Return 0% Drawdown 0% Expectancy 0 Start Equity 100000 End Equity 100000 Net Profit 0% Sharpe Ratio 0 Sortino Ratio 0 Probabilistic Sharpe Ratio 0% Loss Rate 0% Win Rate 0% Profit-Loss Ratio 0 Alpha 0 Beta 0 Annual Standard Deviation 0 Annual Variance 0 Information Ratio -0.461 Tracking Error 0.159 Treynor Ratio 0 Total Fees $0.00 Estimated Strategy Capacity $0 Lowest Capacity Asset Portfolio Turnover 0% |
#region imports using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Globalization; using System.Drawing; using QuantConnect; using QuantConnect.Algorithm.Framework; using QuantConnect.Algorithm.Framework.Selection; using QuantConnect.Algorithm.Framework.Alphas; using QuantConnect.Algorithm.Framework.Portfolio; using QuantConnect.Algorithm.Framework.Portfolio.SignalExports; using QuantConnect.Algorithm.Framework.Execution; using QuantConnect.Algorithm.Framework.Risk; using QuantConnect.Algorithm.Selection; using QuantConnect.Api; using QuantConnect.Parameters; using QuantConnect.Benchmarks; using QuantConnect.Brokerages; using QuantConnect.Configuration; using QuantConnect.Util; using QuantConnect.Interfaces; using QuantConnect.Algorithm; using QuantConnect.Indicators; using QuantConnect.Data; using QuantConnect.Data.Auxiliary; using QuantConnect.Data.Consolidators; using QuantConnect.Data.Custom; using QuantConnect.Data.Custom.IconicTypes; using QuantConnect.DataSource; using QuantConnect.Data.Fundamental; using QuantConnect.Data.Market; using QuantConnect.Data.Shortable; using QuantConnect.Data.UniverseSelection; using QuantConnect.Notifications; using QuantConnect.Orders; using QuantConnect.Orders.Fees; using QuantConnect.Orders.Fills; using QuantConnect.Orders.OptionExercise; using QuantConnect.Orders.Slippage; using QuantConnect.Orders.TimeInForces; using QuantConnect.Python; using QuantConnect.Scheduling; using QuantConnect.Securities; using QuantConnect.Securities.Equity; using QuantConnect.Securities.Future; using QuantConnect.Securities.Option; using QuantConnect.Securities.Positions; using QuantConnect.Securities.Forex; using QuantConnect.Securities.Crypto; using QuantConnect.Securities.CryptoFuture; using QuantConnect.Securities.Interfaces; using QuantConnect.Securities.Volatility; using QuantConnect.Storage; using QuantConnect.Statistics; using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm; using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm; #endregion namespace FirenzeTech { public enum AssetIdx { Hogs = 0, Corn = 1, Soymeal = 2, Count = 3 } public enum PosSize { Hogs = 20, Corn = 8, Soymeal = 3 } public enum OrderState { None, Open, ClosePending, Closed } /*=================================================================================== * H2 position is HE, ZC and ZM orders with 20:8:3 ratio * * * * TODO: * - Open/close orders in steps of 5:2:1 lots * - Close positions that don't confirm to required ratio * - Open limit orders slightly worse than current price to guarantee fills * - Notify on errors * *==================================================================================*/ public class H2PositionInfo { public bool longHogs; /* true if the position opened to buy hogs, sell grains */ public bool longGrains { get { return (!longHogs); } } /* true if the position opened to buy grains, sell hogs */ public OrderTicket[] openTickets; /* tickets created when the order was opened */ public OrderTicket[] closeTickets; /* tickets created to close the order */ public OrderState[] orderState; public static int[] buyRatio = { 20, -8, -3 }; public static int[] sellRatio = { -20, 8, 3 }; public double fillMargin = 0.001; /* factor to move limit orders price slightly below market price so it has a better chance of */ /* being filled. The factor is a percentage of asset price, so 0.001 is 0.1% of asset price. */ /* This works out to be ~$800 for H2 combined price based on 20:8:3 lots */ public H2PositionInfo() { } public H2PositionInfo(SpreadGroup sg, bool buyHogs) : this() { } public bool Update() { return (true); } public bool IsClosed() { return (true); } public bool CloseAtLimit(SpreadGroup sg) { return (true); } public bool CloseAtMarket() { return (true); } private bool CloseOrder(int orderIdx, decimal limitPrice = decimal.MinValue) { return (true); } private void UpdateOrderStatus(int idx) { } } public class OrderExecution { public uint posCount { get ; } /* number of positions in positions list */ public int signedPosCount { get ; } /* signed number of positions (<0 = short hogs, >0 = long hogs) */ public OrderExecution() { } public void ManagePositions(int reqPos, SpreadGroup sg) { } } }
#region imports using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Globalization; using System.Drawing; using QuantConnect; using QuantConnect.Algorithm.Framework; using QuantConnect.Algorithm.Framework.Selection; using QuantConnect.Algorithm.Framework.Alphas; using QuantConnect.Algorithm.Framework.Portfolio; using QuantConnect.Algorithm.Framework.Portfolio.SignalExports; using QuantConnect.Algorithm.Framework.Execution; using QuantConnect.Algorithm.Framework.Risk; using QuantConnect.Algorithm.Selection; using QuantConnect.Api; using QuantConnect.Parameters; using QuantConnect.Benchmarks; using QuantConnect.Brokerages; using QuantConnect.Configuration; using QuantConnect.Util; using QuantConnect.Interfaces; using QuantConnect.Algorithm; using QuantConnect.Indicators; using QuantConnect.Data; using QuantConnect.Data.Auxiliary; using QuantConnect.Data.Consolidators; using QuantConnect.Data.Custom; using QuantConnect.Data.Custom.IconicTypes; using QuantConnect.DataSource; using QuantConnect.Data.Fundamental; using QuantConnect.Data.Market; using QuantConnect.Data.Shortable; using QuantConnect.Data.UniverseSelection; using QuantConnect.Notifications; using QuantConnect.Orders; using QuantConnect.Orders.Fees; using QuantConnect.Orders.Fills; using QuantConnect.Orders.OptionExercise; using QuantConnect.Orders.Slippage; using QuantConnect.Orders.TimeInForces; using QuantConnect.Python; using QuantConnect.Scheduling; using QuantConnect.Securities; using QuantConnect.Securities.Equity; using QuantConnect.Securities.Future; using QuantConnect.Securities.Option; using QuantConnect.Securities.Positions; using QuantConnect.Securities.Forex; using QuantConnect.Securities.Crypto; using QuantConnect.Securities.CryptoFuture; using QuantConnect.Securities.Interfaces; using QuantConnect.Securities.Volatility; using QuantConnect.Storage; using QuantConnect.Statistics; using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm; using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm; #endregion namespace FirenzeTech { public class H2Algorithm : FTAlgorithm { public override DateTime OnInitialize(List<DateTime> h2ContractsList, Resolution dataRate, History histDB) { return (DateTime.MinValue); } public override List<SpreadGroup> OnNewData(Slice slice) { return new List<SpreadGroup>(); } public void UpdateGroupsOnNewPrice(Symbol contractSymbol, double bidPrice, double askPrice, DateTime sliceTime, ref List<SpreadGroup> readyGroups) { } public void AddRelevantFutureContract(Symbol futureSymbol, int futureIndex, DateTime date) { } } }
#region imports using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Globalization; using System.Drawing; using QuantConnect; using QuantConnect.Algorithm.Framework; using QuantConnect.Algorithm.Framework.Selection; using QuantConnect.Algorithm.Framework.Alphas; using QuantConnect.Algorithm.Framework.Portfolio; using QuantConnect.Algorithm.Framework.Portfolio.SignalExports; using QuantConnect.Algorithm.Framework.Execution; using QuantConnect.Algorithm.Framework.Risk; using QuantConnect.Algorithm.Selection; using QuantConnect.Api; using QuantConnect.Parameters; using QuantConnect.Benchmarks; using QuantConnect.Brokerages; using QuantConnect.Configuration; using QuantConnect.Util; using QuantConnect.Interfaces; using QuantConnect.Algorithm; using QuantConnect.Indicators; using QuantConnect.Data; using QuantConnect.Data.Auxiliary; using QuantConnect.Data.Consolidators; using QuantConnect.Data.Custom; using QuantConnect.Data.Custom.IconicTypes; using QuantConnect.DataSource; using QuantConnect.Data.Fundamental; using QuantConnect.Data.Market; using QuantConnect.Data.Shortable; using QuantConnect.Data.UniverseSelection; using QuantConnect.Notifications; using QuantConnect.Orders; using QuantConnect.Orders.Fees; using QuantConnect.Orders.Fills; using QuantConnect.Orders.OptionExercise; using QuantConnect.Orders.Slippage; using QuantConnect.Orders.TimeInForces; using QuantConnect.Python; using QuantConnect.Scheduling; using QuantConnect.Securities; using QuantConnect.Securities.Equity; using QuantConnect.Securities.Future; using QuantConnect.Securities.Option; using QuantConnect.Securities.Positions; using QuantConnect.Securities.Forex; using QuantConnect.Securities.Crypto; using QuantConnect.Securities.CryptoFuture; using QuantConnect.Securities.Interfaces; using QuantConnect.Securities.Volatility; using QuantConnect.Storage; using QuantConnect.Statistics; using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm; using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm; #endregion namespace FirenzeTech { public class H2Constants { } public class H2Strategy : FTStrategy { private List<DateTime> h2ContractDates = new List<DateTime>(); /* list of all required h2 contracts year/month to test, sorted in ascending order */ public override void OnInitialize() { /*================================================================ * Read and parse user supplied parameters *===============================================================*/ string h2DatesStrg = BrokerIF.GetParameter("h2_date_range", "5/2012"); /* default test range is 2008 until current date */ h2ContractDates = Tools.ParseContractDatesParam(h2DatesStrg); if (h2ContractDates == null) { Tools.FatalError($"The supplied H2 contracts dates range \"{h2DatesStrg} \"is invalid.\n" + $"The accepted format is \"mm/yy - mm/yy, ...\""); } } public override void OnNewData(Slice data) { } void AddH2GroupChart(string name) { } void ExecuteTradeLogic(SpreadGroup sg) { } } }
#region imports using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Globalization; using System.Drawing; using QuantConnect; using QuantConnect.Algorithm.Framework; using QuantConnect.Algorithm.Framework.Selection; using QuantConnect.Algorithm.Framework.Alphas; using QuantConnect.Algorithm.Framework.Portfolio; using QuantConnect.Algorithm.Framework.Portfolio.SignalExports; using QuantConnect.Algorithm.Framework.Execution; using QuantConnect.Algorithm.Framework.Risk; using QuantConnect.Algorithm.Selection; using QuantConnect.Api; using QuantConnect.Parameters; using QuantConnect.Benchmarks; using QuantConnect.Brokerages; using QuantConnect.Configuration; using QuantConnect.Util; using QuantConnect.Interfaces; using QuantConnect.Algorithm; using QuantConnect.Indicators; using QuantConnect.Data; using QuantConnect.Data.Auxiliary; using QuantConnect.Data.Consolidators; using QuantConnect.Data.Custom; using QuantConnect.Data.Custom.IconicTypes; using QuantConnect.DataSource; using QuantConnect.Data.Fundamental; using QuantConnect.Data.Market; using QuantConnect.Data.Shortable; using QuantConnect.Data.UniverseSelection; using QuantConnect.Notifications; using QuantConnect.Orders; using QuantConnect.Orders.Fees; using QuantConnect.Orders.Fills; using QuantConnect.Orders.OptionExercise; using QuantConnect.Orders.Slippage; using QuantConnect.Orders.TimeInForces; using QuantConnect.Python; using QuantConnect.Scheduling; using QuantConnect.Securities; using QuantConnect.Securities.Equity; using QuantConnect.Securities.Future; using QuantConnect.Securities.Option; using QuantConnect.Securities.Positions; using QuantConnect.Securities.Forex; using QuantConnect.Securities.Crypto; using QuantConnect.Securities.CryptoFuture; using QuantConnect.Securities.Interfaces; using QuantConnect.Securities.Volatility; using QuantConnect.Storage; using QuantConnect.Statistics; using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm; using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm; #endregion namespace FirenzeTech { /*=================================================================================== * Class to manage historical data * * * * * *==================================================================================*/ public class History { public double[] means = new double[12]; /* Current means for every month chain */ public double[] StdDev = new double[12]; /* Current standard deviation for every month chain */ private H2Chain[] h2Chains = new H2Chain[12]; /* H2 historical data for each month */ /*==================================================================================== * Create historical means database from csv string * * Entry: csvMeans = csv string of historical spread and mean values * startYear = H2 year to start the database from (should be >= 2000) * success = receives true if the operation completed successfully * * Return: None * * Notes: * * TODO: - extract standard deviation (also get csv values that include std dev) * *===================================================================================*/ public History(string csvMeans, int startYear, out bool success) { List<object> colList = Tools.CsvToColList(csvMeans, "FfFfDII", 1); success = ((colList != null) && (colList.Count == 7)); if (success) { success = false; double[] volumeCol = (double[])colList[0]; /* volume column */ double[] sdCol = (double[])colList[1]; /* standard deviation column */ double[] spreadCol = (double[])colList[2]; /* spread column */ double[] meanCol = (double[])colList[3]; /* mean column */ DateTime[] sampleDateCol = (DateTime[])colList[4]; /* sample date column */ int[] groupYearCol = (int[])colList[5]; /* group year column */ int[] groupMonthCol = (int[])colList[6]; /* group month column */ int rowCount = spreadCol.Length; if (groupMonthCol[0] != 1) return; /* month 1 must be the first month in the array */ int currYearStartIdx = 0; /* starting row of the chain month we will extract */ int nextChainStart; /* starting row of the chain next month we will extract */ /*=================================================================================== * currYearStartIdx = starting row of the chain we will extract * nextChainStart = starting row of the next chain we will extract * * loop through each chain month *==================================================================================*/ for (int month = 1; month <= 12; month++) { H2Chain h2Chain = h2Chains[month - 1] = new H2Chain(startYear); if (month == 12) { nextChainStart = Array.LastIndexOf(groupMonthCol, month, rowCount - 1); /* this serached backwards from rowCount - 1 */ if (nextChainStart > 0) nextChainStart++; } else { nextChainStart = Array.IndexOf(groupMonthCol, month + 1, currYearStartIdx); } if ((nextChainStart < 0) || (nextChainStart <= currYearStartIdx)) { return; } /*======================================================= * chain start and end index found * fnd the start of the required year in the chain *======================================================*/ int chainLen = nextChainStart - currYearStartIdx; currYearStartIdx = Array.IndexOf(groupYearCol, startYear, currYearStartIdx, chainLen); if (currYearStartIdx < 0) { return; ; } chainLen = nextChainStart - currYearStartIdx; if (chainLen <= 0) { return; } /*======================================================= * currYearStartIdx = curr year start idx in curr chain * nextChainStart = chain end index + 1 * chainLen = diff between the above two vars * * Loop through years in the chain *======================================================*/ int nextYearStartIdx = 0; for (int yr = startYear; nextYearStartIdx < nextChainStart; yr++) { /*======================================================= * find the end index of current year *======================================================*/ nextYearStartIdx = Array.IndexOf(groupYearCol, yr + 1, currYearStartIdx, chainLen); if (nextYearStartIdx < 0) { /*=================================================================================== * if start of next year was not found, this must be the last year in the chain * and as such, [next chain start - 1] must contain current year *==================================================================================*/ if (groupYearCol[nextChainStart - 1] != yr) { return; } nextYearStartIdx = nextChainStart; } int yearSampleCount = nextYearStartIdx - currYearStartIdx; if (yearSampleCount <= 0) { return; } /*=========================================================== * create and copy samples of the group (i.e. current year) *==========================================================*/ GroupRecords grpData = new GroupRecords(); grpData.sampleDates = sampleDateCol.Skip(currYearStartIdx).Take(yearSampleCount).ToList(); grpData.spreads = spreadCol.Skip(currYearStartIdx).Take(yearSampleCount).ToList(); grpData.means = meanCol.Skip(currYearStartIdx).Take(yearSampleCount).ToList(); grpData.stdDev = sdCol.Skip(currYearStartIdx).Take(yearSampleCount).ToList(); /*======================================================= * fill missing means and std dev at group start from * overlapping days in the previous year *======================================================*/ if (grpData.means[0] == double.MinValue) { /*======================================================= * find last empty mean at current year start *======================================================*/ int overlapCnt = 0; while ((overlapCnt < grpData.means.Count) && (grpData.means[overlapCnt] == double.MinValue)) { overlapCnt++; } int ovStartIdx = currYearStartIdx - overlapCnt; /* overlap start index */ /*============================================================== * if overlap start data doesn't match current year start data *=============================================================*/ if ((ovStartIdx < 0) || (grpData.sampleDates[0] != sampleDateCol[ovStartIdx]) || (groupYearCol[ovStartIdx] != yr - 1) || (groupMonthCol[ovStartIdx - 1] != month)) { if ((overlapCnt == 1) && (grpData.means.Count > 1)) { grpData.means[0] = grpData.means[1]; grpData.stdDev[0] = grpData.stdDev[1]; } else { return; } } else { /*======================================================= * copy mean from last year group end *======================================================*/ for (int i = 0; i < overlapCnt; i++) { if (grpData.sampleDates[i] != sampleDateCol[ovStartIdx + i]) /* make sure dates match */ { return; } grpData.means[i] = meanCol[ovStartIdx + i]; grpData.stdDev[i] = sdCol[ovStartIdx + i]; } } } h2Chain.chainGroups.Add(grpData); currYearStartIdx += yearSampleCount; chainLen-= yearSampleCount; } currYearStartIdx = nextChainStart; } success = true; } } /*==================================================================================== * Return a history record for the specified spread group and date * * Entry: date = date to get the record for (hour not important) * month = h2 month to get the record for (1 - 12) * year = h2 year to get the record for * * Return: history record or null * * Notes: * * TODO: calculate the mean if we have enough history records * *===================================================================================*/ public HistoryRecord GetRecord(DateTime date, int month, int year) { if ((month < 1) || (month > 12)) { Tools.FatalError($"Invalid history month value ({month})"); } return (h2Chains[month - 1].GetRecord(year, date)); } /*==================================================================================== * Check if specified record date is valid * * Entry: date = date to check the record for (hour not important) * month = chain month to check the record for (1 - 12) * year = chain year to check the record for * * Return: true if record date is valid * (date is a weekday and a record exist for the previous or next date) * false if record date is invalid * (date is a weekend or a record doesn't exist for the previous or * next date) * * Notes: This method should typically be used to check if the supplied request * date was valid if GetReord() returns null * *===================================================================================*/ public bool RecordDateValid(DateTime date, int month, int year) { if (GetRecord(date, month, year) == null) { if ((date.DayOfWeek == DayOfWeek.Saturday) || (date.DayOfWeek == DayOfWeek.Sunday)) { return (false); } if ((h2Chains[month - 1].GetRecord(year, date.AddDays(-1)) == null) && (h2Chains[month - 1].GetRecord(year, date.AddDays(1)) == null)) { return (false); } } return (true); } } /*=================================================================================== * The H2Chain class contains daily historical data for an H2 chain (group month) * from ~1978 to the currently active contracts expiry dates * * The records in the arrays are sorted according to the following criteria * - Group Month (1-12) * - Group Year (1978 - current) * - record date within the group (1978 - current) * * Example of the sorted groups in JAN chain: * * Jan 1978 group * first trading date of the group * ... * last trading date of the group * Jan 1979 group * first trading date of the group * ... * last trading date of the group * Jan 1980 group * ... * * Note that the last few trading dates of a group may overlap the first few * trading dates of the next year group. The overlapping dates in the newer year * group will not contain any mean values as those means are included in the * overlapped days of the older year group * * * *==================================================================================*/ public class H2Chain { public int startYear { get; } /* starting year for groups in chainGroups (1978--) */ public List<GroupRecords> chainGroups; /* list of GroupRecords for all available years in the chain */ /* groupYears[0] = groups records for first year in the chain */ /* groupYears[n] = groups records for first nth year in the chain */ public H2Chain(int startYr) { chainGroups = new List<GroupRecords>(); startYear = startYr; } /*==================================================================================== * return record index from group year and record date * * Entry: grpYear = year of the required group in the chain * sampDate = date of the record within the group * * Return: index of requested record or -1 if noty found * * Notes: * *===================================================================================*/ public int GetRecordIndex(int grpYear, DateTime sampDate) { int yearIdx = grpYear - startYear; if ((yearIdx < 0) || (yearIdx >= chainGroups.Count)) { return (-1); } return (chainGroups[yearIdx].GetRecordIndexFromDate(sampDate)); } /*==================================================================================== * return record from group year and record date * * Entry: grpYear = year of the required group in the chain * sampDate = date of the record within the group * * Return: record or null * * Notes: * *===================================================================================*/ public HistoryRecord GetRecord(int grpYear, DateTime sampDate) { int yearIdx = grpYear - startYear; if ((yearIdx < 0) || (yearIdx >= chainGroups.Count)) { return (null); } return (chainGroups[yearIdx].GetRecordFromDate(sampDate)); } } /*=================================================================================== * database for any single spread groups (e.g., JAN 2024) * * * * *==================================================================================*/ public class GroupRecords { public int lastReqSampleIdx { get; private set; } /* index of last requested sample. -1 if not defined */ public List<DateTime> sampleDates; /* date of each sample in the group */ public List<double> spreads; /* daily spread value */ public List<double> means; /* daily historical mean */ public List<double> stdDev; /* std deviation */ public GroupRecords() { sampleDates = new List<DateTime>(); spreads= new List<double>(); means= new List<double>(); stdDev= new List<double>(); lastReqSampleIdx = -1; } /*==================================================================================== * return record index from record date * * Entry: sampDate = date of the record within the group. must exactly match. * * Return: index of requested record or -1 if not found * * Notes: * *===================================================================================*/ public int GetRecordIndexFromDate(DateTime sampDate) { sampDate = new DateTime(sampDate.Year, sampDate.Month, sampDate.Day); if (lastReqSampleIdx >= 0) { if (sampDate >= sampleDates[lastReqSampleIdx]) { if (sampDate == sampleDates[lastReqSampleIdx]) { return (lastReqSampleIdx); } lastReqSampleIdx++; if (lastReqSampleIdx < sampleDates.Count) { if (sampDate == sampleDates[lastReqSampleIdx]) { return (lastReqSampleIdx); } } } } lastReqSampleIdx = sampleDates.BinarySearch(sampDate); return (lastReqSampleIdx); } public HistoryRecord GetRecordFromDate(DateTime sampDate) { int idx = GetRecordIndexFromDate(sampDate); if (idx >= 0) { return (new HistoryRecord(sampleDates[idx], means[idx], spreads[idx], stdDev[idx])); } return(null); } } public class HistoryRecord { public DateTime date; public double mean; public double stdDev; public double spread; public bool valid; public HistoryRecord(DateTime dt, double mn, double sprd, double stdv) { date = dt; mean = mn; spread = sprd; stdDev = stdv; } } }
#region imports using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Globalization; using System.Drawing; using QuantConnect; using QuantConnect.Algorithm.Framework; using QuantConnect.Algorithm.Framework.Selection; using QuantConnect.Algorithm.Framework.Alphas; using QuantConnect.Algorithm.Framework.Portfolio; using QuantConnect.Algorithm.Framework.Execution; using QuantConnect.Algorithm.Framework.Risk; using QuantConnect.Algorithm.Selection; using QuantConnect.Parameters; using QuantConnect.Benchmarks; using QuantConnect.Brokerages; using QuantConnect.Util; using QuantConnect.Interfaces; using QuantConnect.Algorithm; using QuantConnect.Indicators; using QuantConnect.Data; using QuantConnect.Data.Consolidators; using QuantConnect.Data.Custom; using QuantConnect.DataSource; using QuantConnect.Data.Fundamental; using QuantConnect.Data.Market; using QuantConnect.Data.UniverseSelection; using QuantConnect.Notifications; using QuantConnect.Orders; using QuantConnect.Orders.Fees; using QuantConnect.Orders.Fills; using QuantConnect.Orders.Slippage; using QuantConnect.Scheduling; using QuantConnect.Securities; using QuantConnect.Securities.Equity; using QuantConnect.Securities.Future; using QuantConnect.Securities.Option; using QuantConnect.Securities.Forex; using QuantConnect.Securities.Crypto; using QuantConnect.Securities.Interfaces; using QuantConnect.Storage; using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm; using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm; #endregion namespace FirenzeTech { public class FirenzePlatform : QCAlgorithm { private static List<FTStrategy> strategyList = new List<FTStrategy>(); public override void Initialize() { Tools.Init(this); H2Strategy h2 = new H2Strategy(); h2.Init(this, "H2"); strategyList.Add(h2); foreach (FTStrategy stratX in strategyList) { stratX.OnInitialize(); } } public override void OnData(Slice data) { } public override void OnEndOfAlgorithm() { } } abstract public class FTStrategy { public static QCAlgorithm BrokerIF { get; private set; } /* broker interface */ public string Name { get; private set; } /* Strategy name */ public void Init(QCAlgorithm b, string nm) { if (Name == null) { BrokerIF = b; Name = nm; } } public abstract void OnInitialize(); /* called once on strategy startup. Must be implemented by derived strategies */ public abstract void OnNewData(Slice data); /* called on every new quote (tick or bar). Must be implemented by derived strategies */ public virtual void OnTerminate() /* called when the strategy terminates */ { } } abstract public class FTAlgorithm { public static QCAlgorithm BrokerIF { get; private set; } /* broker interface */ public string Name { get; private set; } /* algorithm name */ public void Init(QCAlgorithm b, string nm) { if (Name == null) { BrokerIF = b; Name = nm; } } public abstract DateTime OnInitialize(List<DateTime> h2ContractsList, Resolution dataRate, History histDB); /* called once on strategy startup. Must be implemented by derived algorithms */ /* returns the backtest start date for the supllied h2 contracts */ public abstract List<SpreadGroup> OnNewData(Slice data); /* called on every new quote (tick or bar). Must be implemented by derived algorithms */ /* returns a list of the spread groups that has a new h2 signal value, or null if none */ public virtual void OnTerminate() /* called when the strategy terminates */ { } } }
#region imports using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Globalization; using System.Drawing; using QuantConnect; using QuantConnect.Algorithm.Framework; using QuantConnect.Algorithm.Framework.Selection; using QuantConnect.Algorithm.Framework.Alphas; using QuantConnect.Algorithm.Framework.Portfolio; using QuantConnect.Algorithm.Framework.Portfolio.SignalExports; using QuantConnect.Algorithm.Framework.Execution; using QuantConnect.Algorithm.Framework.Risk; using QuantConnect.Algorithm.Selection; using QuantConnect.Api; using QuantConnect.Parameters; using QuantConnect.Benchmarks; using QuantConnect.Brokerages; using QuantConnect.Configuration; using QuantConnect.Util; using QuantConnect.Interfaces; using QuantConnect.Algorithm; using QuantConnect.Indicators; using QuantConnect.Data; using QuantConnect.Data.Auxiliary; using QuantConnect.Data.Consolidators; using QuantConnect.Data.Custom; using QuantConnect.Data.Custom.IconicTypes; using QuantConnect.DataSource; using QuantConnect.Data.Fundamental; using QuantConnect.Data.Market; using QuantConnect.Data.Shortable; using QuantConnect.Data.UniverseSelection; using QuantConnect.Notifications; using QuantConnect.Orders; using QuantConnect.Orders.Fees; using QuantConnect.Orders.Fills; using QuantConnect.Orders.OptionExercise; using QuantConnect.Orders.Slippage; using QuantConnect.Orders.TimeInForces; using QuantConnect.Python; using QuantConnect.Scheduling; using QuantConnect.Securities; using QuantConnect.Securities.Equity; using QuantConnect.Securities.Future; using QuantConnect.Securities.Option; using QuantConnect.Securities.Positions; using QuantConnect.Securities.Forex; using QuantConnect.Securities.Crypto; using QuantConnect.Securities.CryptoFuture; using QuantConnect.Securities.Interfaces; using QuantConnect.Securities.Volatility; using QuantConnect.Storage; using QuantConnect.Statistics; using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm; using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm; #endregion namespace FirenzeTech { public enum PriceComponent { Bid, Ask, Median } /*=================================================================================== * * * * TODO: * - Which spread value (at 1:00pm?) should be used to calculate the mean * *==================================================================================*/ public class SpreadStatus { public OrderExecution PositionMgmt; /* current Positions */ public PriceComponent priceComponent; /* which price to use to create the spread */ public bool LevelsFrozen; /* true if trigger levels frozen */ public double Mean { get { return (LevelsFrozen ? FrozenMean : CurrentMean); } } public double StdDev { get { return (LevelsFrozen ? FrozenStdDeviation : StdDeviation); } } public double TrigWidth { get { return (LevelsFrozen ? FrozenTriggerWidth : TriggerWidth); } } private History historyDB; /* mean history database */ private int meanErrors = 0; /* counter for consecutive mean calculation errors */ private DateTime lastUpdateTime; /* time of last mean update */ private double CurrentMean; /* rolling mean over the past 18 month for this group, calculated at end of the day */ private double StdDeviation; /* standard deviation for this group, calculated daily */ private double TriggerWidth; /* trigger level width for this group, = stdDeviation * 0.85 */ private double FrozenMean; /* Frozen mean. = CurrentMean if not frozen */ private double FrozenStdDeviation; /* Frozen standard deviation. = StdDeviation if not frozen */ private double FrozenTriggerWidth; /* Frozen level width. = TriggerWidth if not frozen */ public SpreadStatus(History histDB) { PositionMgmt = new OrderExecution(); priceComponent = PriceComponent.Median; historyDB = histDB; lastUpdateTime = DateTime.MinValue; CurrentMean = 0; StdDeviation = 0; TriggerWidth = 0; FrozenMean = 0; FrozenStdDeviation = 0; FrozenTriggerWidth = 0; LevelsFrozen = false; } /*==================================================================================== * Find the level index (0 -> STDEV levels + 1) corresponding to a spread value, * taking into account if the levels are frozen * * Entry: spreadVal = spread value to return its level * stdevLevels = standard deviation levels (1--) * onBoundary = receives true if spreadVal is on returned level boundary * * Return: Level starting at 0 * Level 0 = between mean and 1st STDEV * Level 1 = between 1st STDEV and 2nd STDEV * if onBoundary is true then: * if spreadVal > Mean, then spreadVal is on level lower boundary * if spreadVal > Mean, then spreadVal is on level higher boundary * * Notes: Example * * Level Returned * Index Level OnBoundary * * 3 * -------*----- 3 true * 2 * ------*------ 3 true * 1 * ------------- * 0 * 0 * ===*========= 0 true * 0 * 0 true * ------------- * 1 * 1 * -------*----- 2 true * 2 * 2 * ---------*--- 3 true * 3 * *===================================================================================*/ public int FindCurrentLevel(double spreadVal, int stdDevLevels, out bool onBoundary) { int currLevelIdx = 0; /* start with mean level */ double currLevelMin; /* start with mean value */ double currLevelMax; onBoundary = false; if (spreadVal >= Mean) { currLevelMin = Mean; currLevelMax = Mean + TriggerWidth; while (currLevelIdx <= stdDevLevels) /* levels 0 - 2 */ { if (spreadVal < currLevelMax) { if (spreadVal == currLevelMin) { onBoundary = true; } break; } currLevelMin = currLevelMax; currLevelMax += TrigWidth; currLevelIdx++; } } else { currLevelMin = Mean - TriggerWidth; currLevelMax = Mean; while (currLevelIdx <= stdDevLevels) /* level 0 - 2 */ { if (spreadVal > currLevelMin) { if (spreadVal == currLevelMax) { onBoundary = true; } break; } currLevelMax = currLevelMin; currLevelMin -= TriggerWidth; currLevelIdx++; } } return (currLevelIdx); } /*==================================================================================== * Should be called at the end of every trading day (or start of a new trading day) * to calculate the mean and STDEV for the day that ended * * Entry: lastDate = last day date * groupId = group month/year * * Return: * * Notes: * *===================================================================================*/ public void DateChanged(DateTime lastDate, DateTime groupId) { if (lastDate > DateTime.MinValue) { HistoryRecord hData = historyDB.GetRecord(lastDate, groupId.Month, groupId.Year); if (hData == null) { meanErrors++; if (historyDB.RecordDateValid(lastDate, groupId.Month, groupId.Year)) { Tools.WarningMessage($"History record for H2 group {groupId.ToString("MM/yyyy")} on {lastDate.ToString("dd/MM/yyyy")} is missing"); } else { Tools.ErrorMessage($"********* History request for H2 group {groupId.ToString("MM/yyyy")} on {lastDate.ToString("dd/MM/yyyy")} is invalid *********"); } if ((CurrentMean == 0) || (meanErrors > 3)) /* if 3 consecutive days with errors, generate an exception */ { //Tools.FatalError($"Failed to obtain or calculate mean value for H2 group {groupId.ToString("MM/yyyy")} on " + lastDate.ToString("dd/MM/yyyy")); } } else { meanErrors = 0; CurrentMean = hData.mean; StdDeviation = hData.stdDev; TriggerWidth = StdDev * 0.85; } } } /*==================================================================================== * Should be called at the start of a new trading day * to get the database spread value for the day that ended * * Entry: date = new day date * groupId = SpreadGroup month/year *===================================================================================*/ public double GetDatabaseSpread(DateTime date, DateTime groupId) { if (date > DateTime.MinValue) { HistoryRecord hData = historyDB.GetRecord(date, groupId.Month, groupId.Year); if (hData == null) { if (historyDB.RecordDateValid(date, groupId.Month, groupId.Year)) { Tools.WarningMessage($"History record for H2 group {groupId.ToString("MM/yyyy")} on {date.ToString("dd/MM/yyyy")} is missing"); } else { Tools.ErrorMessage($"********* History request for H2 group {groupId.ToString("MM/yyyy")} on {date.ToString("dd/MM/yyyy")} is invalid *********"); } } else { return hData.spread; } } return 0; } } /*=================================================================================== * A spread group defines a specific month and year of H2, for example JAN 2022 * * * * *==================================================================================*/ public class SpreadGroup { public DateTime DateTag { get; } /* H2 month this group is for (e.g., 5/2024), the date is always 1 */ public string Name { get { return(DateTag.ToString("MMM_yy")); } } public readonly ContractSpec ContrSpec; /* the specifications of contracts belonging to this spread */ public DateTime LastSampleDate { get; private set; } /* date & time of the last price recorded in _contractPrices array. Init to DateTime.MinValue */ public double Value { get; private set; } /* current h2 spread value for tis group, calculated on every tick received for the group */ public SpreadStatus Status; /* status of this group spread */ private double[] LastBidPrices; /* stores last known bid price of each asset */ private double[] LastAskPrices; /* stores last known ask price of each asset */ private List<Symbol> ContractSymbols; /* list of underlying contracts symbols */ private DateTime minExpiryDate; /* earliest expiry date of contract symbols */ private Resolution dataRes; /* resolution of incoming data */ private int _availablePrices; /* number of available contract prices in _contractPrices */ private double[] _contractPrices; /* double.MinValue indicate empty entry */ private bool heAvailable; /* true if hogs (HE) contract received any ticks */ private double databaseValue; /* h2 spread value on LastSampleDate taken from the database */ public SpreadGroup(ContractSpec cSpec, DateTime name, Resolution reqRes, History histDB) { } public override string ToString() { return ($"SpreadGroup{{name:{Name}, minExpiry:{minExpiryDate.ToShortDateString()}, currDate:{LastSampleDate.ToShortDateString()}, prices:[{string.Join(",", _contractPrices.Select(n => n == double.MinValue ? "---" : n.ToString()))}]}}"); } public bool IsIndexPriceReady(int index) { return true; } public bool IsIndexSymbolReady(int index) { return true; } public void UpdateSymbol(Symbol symbol, int index, DateTime symbolExpiry) { } public bool GetContractPrice(string ticker, ref double bid, ref double ask) { return true; } public Symbol GetContractSymbol(string ticker) { return (Symbol.None); } public bool UpdatePrice(double price, int index, DateTime date) { return (false); } } public class ContractSpec { public string Name { get; } /* group name. arbitrary */ public readonly List<string> SymbolStrings; /* names of contracts in the group (e.g., HE, ZC, ZM) */ public readonly Dictionary<string, int> SymbolStringToIndex; /* dictionary of Symbol name to index */ public readonly List<Dictionary<int, List<int>>> ContractToGroup; /* list of dictionaries <int, List<int>> */ public readonly List<int> Multipliers; public readonly List<int> PriceToDollarMultipliers; /* convert price to $ value */ // public readonly List<Symbol> FutureSymbols; /* continuous future symbols */ public ContractSpec(string name) { } public int GetIndexOfTicker(string ticker) { return 0; } } }
#region imports using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Globalization; using System.Drawing; using QuantConnect; using QuantConnect.Algorithm.Framework; using QuantConnect.Algorithm.Framework.Selection; using QuantConnect.Algorithm.Framework.Alphas; using QuantConnect.Algorithm.Framework.Portfolio; using QuantConnect.Algorithm.Framework.Portfolio.SignalExports; using QuantConnect.Algorithm.Framework.Execution; using QuantConnect.Algorithm.Framework.Risk; using QuantConnect.Algorithm.Selection; using QuantConnect.Api; using QuantConnect.Parameters; using QuantConnect.Benchmarks; using QuantConnect.Brokerages; using QuantConnect.Configuration; using QuantConnect.Util; using QuantConnect.Interfaces; using QuantConnect.Algorithm; using QuantConnect.Indicators; using QuantConnect.Data; using QuantConnect.Data.Auxiliary; using QuantConnect.Data.Consolidators; using QuantConnect.Data.Custom; using QuantConnect.Data.Custom.IconicTypes; using QuantConnect.DataSource; using QuantConnect.Data.Fundamental; using QuantConnect.Data.Market; using QuantConnect.Data.Shortable; using QuantConnect.Data.UniverseSelection; using QuantConnect.Notifications; using QuantConnect.Orders; using QuantConnect.Orders.Fees; using QuantConnect.Orders.Fills; using QuantConnect.Orders.OptionExercise; using QuantConnect.Orders.Slippage; using QuantConnect.Orders.TimeInForces; using QuantConnect.Python; using QuantConnect.Scheduling; using QuantConnect.Securities; using QuantConnect.Securities.Equity; using QuantConnect.Securities.Future; using QuantConnect.Securities.Option; using QuantConnect.Securities.Positions; using QuantConnect.Securities.Forex; using QuantConnect.Securities.Crypto; using QuantConnect.Securities.CryptoFuture; using QuantConnect.Securities.Interfaces; using QuantConnect.Securities.Volatility; using QuantConnect.Storage; using QuantConnect.Statistics; using QCAlgorithmFramework = QuantConnect.Algorithm.QCAlgorithm; using QCAlgorithmFrameworkBridge = QuantConnect.Algorithm.QCAlgorithm; #endregion namespace FirenzeTech { public enum LogFilter { All = 0xFF, None = 0, Log = 1, Debug = 2, Warning = 4, Error = 8 } public class TradeHours { public TimeSpan startMin; public TimeSpan endMin; public TimeSpan startHour; public TimeSpan endHour; public TradeHours(int hrStart, int minStart, int hrEnd, int minEnd) { startMin = new TimeSpan(hrStart, minStart, 0); endMin = new TimeSpan(hrEnd, minEnd, 0); startHour = new TimeSpan(hrStart + (minStart == 0 ? 0 : 1), 0, 0); endHour = new TimeSpan(hrEnd + (minEnd == 0 ? 0 : 1), 0, 0); } } public static class Tools { public static QCAlgorithm BrokerIF { get; private set; } /* broker interface */ private static int[] heValidMonths = new int[] { 2, 4, 5, 6, 7, 8, 10, 12 }; private static int[] heListMonth = new int[] { 0, 8, 0, 10, 12, 12, 2, 4, 0, 5, 0, 6 }; /* for contract month 1-2, */ private static readonly TradeHours tradeHoursEST = new TradeHours(9, 30, 14, 5); private static readonly TradeHours tradeHoursCT = new TradeHours(8, 30, 13, 5); private static readonly TradeHours tradeHoursUTC = new TradeHours(14, 30, 19, 5); private static readonly TimeZoneInfo estZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time"); private static readonly Symbol hogSym = Symbol.Create(Futures.Meats.LeanHogs, SecurityType.Future, Market.CME); private static readonly Symbol cornSym = Symbol.Create(Futures.Grains.Corn, SecurityType.Future, Market.CME); private static readonly Symbol soyMealSym = Symbol.Create(Futures.Grains.SoybeanMeal, SecurityType.Future, Market.CME); /* Dictionary to map contract month code to their corresponding month numbers */ private static LogFilter logFilter = LogFilter.All; public static int logLevel = 1; /*==================================================================================== * Set the broker interface * * Entry: broker = broker interface * * Return: none * * Notes: * *===================================================================================*/ public static void Init(QCAlgorithm broker) { if (BrokerIF == null) { BrokerIF = broker; } } public static void LogMessage(string msg, int level = 0) { if (((logFilter & LogFilter.Log) == LogFilter.Log) && (level <= logLevel)) { BrokerIF.Log(msg); } } public static void DebugMessage(string msg) { if ((logFilter & LogFilter.Debug) == LogFilter.Debug) { BrokerIF.Debug(msg); } } public static void WarningMessage(string msg) { if ((logFilter & LogFilter.Warning) == LogFilter.Warning) { BrokerIF.Debug(msg); } } public static void ErrorMessage(string msg) { if ((logFilter & LogFilter.Error) == LogFilter.Error) { BrokerIF.Error(msg); } } public static void SetLogFlags(string levels) { LogFilter reqLev = LogFilter.None; levels = levels.Trim().ToLower(); foreach (char c in levels) { if (c == 'l') reqLev |= LogFilter.Log; else if (c == 'd') reqLev |= LogFilter.Debug; else if (c == 'w') reqLev |= LogFilter.Warning; else if (c == 'e') reqLev |= LogFilter.Error; } logFilter = reqLev; } public static void FatalError(string msg) { const string frame = "\n=========================================================\n"; throw new ArgumentException(frame + msg + frame); } public static uint ElapsedMs(DateTime startTime) { return (0); } public static bool InsideTradingHoursCT(DateTime ctDate, Resolution res) { return(InsideTradingHours(ctDate, res, tradeHoursCT)); } public static bool InsideTradingHoursEST(DateTime estDate, Resolution res) { return (InsideTradingHours(estDate, res, tradeHoursEST)); } public static bool InsideTradingHoursUTC(DateTime utcDate, Resolution res) { if (DateTime.Now.IsDaylightSavingTime()) { utcDate = utcDate.AddHours(1); } return (InsideTradingHours(utcDate, res, tradeHoursUTC)); } private static bool InsideTradingHours(DateTime utcDate, Resolution res, TradeHours timeLimit) { switch (res) { case Resolution.Hour: return ((utcDate.TimeOfDay >= timeLimit.startHour) && (utcDate.TimeOfDay <= timeLimit.endHour)); case Resolution.Daily: return (true); default: return ((utcDate.TimeOfDay >= timeLimit.startMin) && (utcDate.TimeOfDay <= timeLimit.endMin)); } } public static bool HEValidMonth(int month) { return (heValidMonths.Contains(month)); } public static DateTime? HEFirstTradingDate(DateTime dt) { int listMonth = heListMonth[dt.Month]; if (listMonth == 0) { return (null); } int totMonths = 12 - listMonth + dt.Month; int listYear = (dt.Year - 1) - (totMonths <= 12 ? 1 : 0); return (new DateTime(listYear, listMonth, 1)); } public static List<Symbol> GetFuturesContractsList(string futuresCode, DateTime startDate, DateTime endDate) { Symbol futuresSym; if (futuresCode == Futures.Meats.LeanHogs) futuresSym = hogSym; else if (futuresCode == Futures.Grains.Corn) futuresSym = cornSym; else if (futuresCode == Futures.Grains.SoybeanMeal) futuresSym = soyMealSym; else return (null); List<Symbol> symList = new List<Symbol>(); while (startDate <= endDate) { var contractList = BrokerIF.FutureChainProvider.GetFutureContractList(futuresSym, startDate); foreach (var contractX in contractList) { if (!symList.Contains(contractX)) { if ((contractX.ID.Date >= startDate) && (contractX.ID.Date <= endDate)) { symList.Add(contractX); } } } if (startDate == endDate) { break; } startDate += new TimeSpan(6 * 30, 0, 0, 0); if (startDate > endDate) { startDate = endDate; } } return (symList); } public static List<DateTime> ParseDateRanges(string datesStrg) { string[] ranges = datesStrg.Split(','); List<DateTime> rangePairs = new List<DateTime>(); for (int i = 0; i < ranges.Length; i++) { string[] range = ranges[i].Trim().Split('-'); if ((range.Length <= 0) || (range.Length > 2)) return (null); DateTime? rStart = ParseDateStrg(range[0]); if (rStart == null) return (null); /*======================================================= * if a single month, add full chain *======================================================*/ if (range.Length == 1) { DateTime chainYear = (DateTime)rStart; while (chainYear.Year < DateTime.Now.Year) { rangePairs.Add(chainYear); rangePairs.Add(chainYear); chainYear = chainYear.AddYears(1); } } else { DateTime? rEnd = String.IsNullOrEmpty(range[1]) ? DateTime.Now : ParseDateStrg(range[1]); if ((rEnd == null) || (rStart > rEnd)) return (null); rangePairs.Add((DateTime)rStart); rangePairs.Add((DateTime)rEnd); } } return (rangePairs.Count == 0 ? null : rangePairs); } public static List<DateTime> ParseContractDatesParam(string datesStrg) { List<DateTime> monthList = null; List<DateTime> rangePairs = ParseDateRanges(datesStrg); if (rangePairs != null) { monthList = new List<DateTime>(); for (int i = 0; i < rangePairs.Count; i += 2) { DateTime rStart = rangePairs[i]; DateTime rEnd = rangePairs[i + 1]; for (int yr = rStart.Year; yr <= rEnd.Year; yr++) { int strMonth = (yr == rStart.Year ? rStart.Month : 1); int endMonth = (yr == rEnd.Year ? rEnd.Month : 12); for (int mon = strMonth; mon <= endMonth; mon++) { DateTime newMon = new DateTime(yr, mon, 1); if (monthList.Contains(newMon)) { return (null); } monthList.Add(newMon); } } } if (monthList.Count == 0) { monthList = null; } } if (monthList != null) { monthList.Sort(); } return (monthList); } private static DateTime? ParseDateStrg(string dateStrg) { try { string[] yrMon = dateStrg.Trim().Split('/'); if (yrMon.Length != 2) return (null); int month = int.Parse(yrMon[0]); int year = int.Parse(yrMon[1]); if ((month < 1) || (month > 12)) return (null); if (year < 100) { year += (year >= 70 ? 1900 : 2000); } return (new DateTime(year, month, 1)); } catch { return (null); } } public static List<object> CsvToColList(string csvStrg, string colsTypes, int rowsToIgnore) { try { string[] rows = csvStrg.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries); colsTypes = colsTypes.Trim().Replace(" ", ""); int nofRows = rows.Length; int nofCols = colsTypes.Length; List<int[]> intArrList = new List<int[]>(); List<double[]> doubleArrList = new List<double[]>(); List<string[]> stringArrList = new List<string[]>(); List<DateTime[]> dateArrList = new List<DateTime[]>(); /*======================================================= * create columns arrays based on requested types *======================================================*/ int reqRows = nofRows - rowsToIgnore; if (reqRows <= 0) { return(null); } string genType = colsTypes.ToLower(); for (int colIdx = 0; colIdx < nofCols; colIdx++) { if (genType[colIdx] == 'i') intArrList.Add(new int[reqRows]); else if (genType[colIdx] == 'f') doubleArrList.Add(new double[reqRows]); else if (genType[colIdx] == 'd') dateArrList.Add(new DateTime[reqRows]); else if (genType[colIdx] == 's') stringArrList.Add(new string[reqRows]); else if (genType[colIdx] != '-') return (null); } int intColIdx; int doubleColIdx; int strgColIdx; int dateColIdx; /*======================================================= * for each row in the csv file *======================================================*/ for (int rowIdx = 0; rowIdx < reqRows; rowIdx++) { /*======================================================= * get row contents as an array of strings *======================================================*/ string[] rowCols = rows[rowIdx + rowsToIgnore].Split(','); int intRes; double doubleRes; DateTime dtRes; intColIdx = doubleColIdx = strgColIdx = dateColIdx = 0; /*======================================================= * for each column in the row *======================================================*/ for (int colIdx = 0; colIdx < nofCols; colIdx++) { string colStrg = rowCols[colIdx].Trim(); /*================================================================== * Determine the column type from colStrg and parse accordingly *=================================================================*/ switch (colsTypes[colIdx]) { case 'i': case 'I': if (int.TryParse(colStrg, out intRes)) { intArrList[intColIdx][rowIdx] = intRes; } else if (string.IsNullOrEmpty(colStrg) && (colsTypes[colIdx] == 'i')) { intArrList[intColIdx][rowIdx] = int.MinValue; } else { return (null); } intColIdx++; break; case 'f': case 'F': if (double.TryParse(colStrg, NumberStyles.Float, CultureInfo.InvariantCulture, out doubleRes)) { doubleArrList[doubleColIdx][rowIdx] = doubleRes; } else if (string.IsNullOrEmpty(colStrg) && (colsTypes[colIdx] == 'f')) { doubleArrList[doubleColIdx][rowIdx] = double.MinValue; } else { return (null); } doubleColIdx++; break; case 's': case 'S': if (string.IsNullOrEmpty(colStrg) && (colsTypes[colIdx] == 's')) { stringArrList[strgColIdx][rowIdx] = colStrg; } else { return (null); } strgColIdx++; break; case 'd': case 'D': if (DateTime.TryParse(colStrg, out dtRes)) { dateArrList[dateColIdx][rowIdx] = dtRes; } else if (string.IsNullOrEmpty(colStrg) && (colsTypes[colIdx] == 'd')) { dateArrList[dateColIdx][rowIdx] = DateTime.MinValue; } else { return (null); } dateColIdx++; break; case '-': break; default: return (null); } } } /*======================================================= * create output list *======================================================*/ List<object> result = new List<object>(); intColIdx = doubleColIdx = strgColIdx = dateColIdx = 0; for (int colIdx = 0; colIdx < nofCols; colIdx++) { switch (colsTypes[colIdx]) { case 'i': case 'I': result.Add((object)intArrList[intColIdx++]); break; case 'f': case 'F': result.Add((object)doubleArrList[doubleColIdx++]); break; case 's': case 'S': result.Add((object)stringArrList[strgColIdx++]); break; case 'd': case 'D': result.Add((object)dateArrList[dateColIdx++]); break; default: break; } } return (result); } catch { return (null); } } } }