public bool SellPolicy(StockEntity currentStockEntity, StockEntity lastBoughtStockEntity, StockEntity lastSoldStockEntity, List<StockEntity> sortedAllStockEntities) { if (currentStockEntity.Close == 0) { return false; } bool biasSellDecesion = false; if (currentStockEntity.GetMA(20) != 0) { double bias = (currentStockEntity.GetMA(5) - currentStockEntity.GetMA(20)) / currentStockEntity.GetMA(5); if (bias > tooStrongTheashold) { biasSellDecesion = true; int index = sortedAllStockEntities.FindIndex(entity => entity.RowKey == currentStockEntity.RowKey); if (index > 1) { var lastStockEntity = sortedAllStockEntities[index - 1]; if ((currentStockEntity.Close - lastStockEntity.Close) / lastStockEntity.Close > 0.05) { Console.WriteLine("Stock is too strong, ignore sell decesion."); biasSellDecesion = false; } } } else if (bias < balenceThreshold && bias >= tooWeakThreshold) { biasSellDecesion = true; } } return biasSellDecesion; }
public bool SellPolicy(StockEntity currentStockEntity, StockEntity lastBoughtStockEntity, StockEntity lastSoldStockEntity, List<StockEntity> sortedAllStockEntities) { bool sc1 = MARules.MA5AboveMA20(currentStockEntity, 0.1) && (MARules.PriceBelowMA5(currentStockEntity, 0.1) || MARules.PriceBelowMA20(currentStockEntity, 0)); bool sc2 = !MARules.MA5AboveMA20(currentStockEntity, 0.1) && MARules.PriceBelowMA5(currentStockEntity, 0.02); bool sc3 = VolumeRules.PriveAboveButVolumeBelowLastBought(currentStockEntity, lastBoughtStockEntity, 0.1); return (sc1 || sc2 || sc3); }
public bool BuyPolicy(StockEntity currentStockEntity, StockEntity lastBoughtStockEntity, StockEntity lastSoldStockEntity, List<StockEntity> sortedAllStockEntities) { if (currentStockEntity.Close == 0) { return false; } bool biasBuyDecesion = false; if (currentStockEntity.GetMA(20) == 0) { biasBuyDecesion = true; } else { double bias = (currentStockEntity.GetMA(5) - currentStockEntity.GetMA(20)) / currentStockEntity.GetMA(5); if (bias <= tooStrongTheashold && bias >= balenceThreshold) { biasBuyDecesion = true; } else if (bias < tooWeakThreshold) { biasBuyDecesion = true; } } return biasBuyDecesion; }
public bool BuyPolicy(StockEntity currentStockEntity, StockEntity lastBoughtStockEntity, StockEntity lastSoldStockEntity, List<StockEntity> sortedAllStockEntities) { bool bc1 = MARules.PriceAboveMA5(currentStockEntity, 0); bool bc2 = MARules.MA5Rising(currentStockEntity, sortedAllStockEntities, 5); bool bc3 = MARules.PriceAboveMA20(currentStockEntity, 0); bool bc4 = PriceRules.PriceDiffAboveDelta(currentStockEntity, lastSoldStockEntity, 0.1); bool bc5 = VolumeRules.VolumeAboveLast5(currentStockEntity, lastSoldStockEntity, sortedAllStockEntities, 0.1); bool bc6 = MARules.MA20Rising(currentStockEntity, sortedAllStockEntities, 3); return (bc1 && bc2 && bc3 && bc4 && bc5 && bc6); }
public bool SellPolicy(StockEntity currentStockEntity, StockEntity lastBoughtStockEntity, StockEntity lastSoldStockEntity, List<StockEntity> sortedAllStockEntities) { bool sc1 = !(MARules.MA20Rising(currentStockEntity, sortedAllStockEntities, 5) && MARules.PriceAboveMA20(currentStockEntity, 0.05)); bool sc2 = !(!MARules.MA20Rising(currentStockEntity, sortedAllStockEntities, 5) && MARules.PriceAboveMA20(currentStockEntity, 0)); bool rtn = (sc1 || sc2); if (rtn) { Console.WriteLine("{0}: Sell condition met: {1},{2}", currentStockEntity.Date.ToString("yyyy-MM-dd"), sc1, sc2); } return rtn; }
public static bool PriceDiffAboveDelta(StockEntity stockEntity, StockEntity lastSoldStockEntity, double delta) { if (lastSoldStockEntity == null) return true; if (Math.Abs(stockEntity.Close - lastSoldStockEntity.Close) / stockEntity.Close >= delta) { return true; } else { return false; } }
public bool SellPolicy(StockEntity currentStockEntity, StockEntity lastBoughtStockEntity, StockEntity lastSoldStockEntity, List<StockEntity> sortedAllStockEntities) { bool sc1 = MARules.MA5AboveMA20(currentStockEntity, 0.1) && (MARules.PriceBelowMA5(currentStockEntity, 0.1) || MARules.PriceBelowMA20(currentStockEntity, 0)); double belowMA5Delta = 0.05; if (VolumeRules.VolumeBelowLastBought(currentStockEntity, lastBoughtStockEntity, 0.6)) belowMA5Delta = 0.02; bool sc2 = !MARules.MA5AboveMA20(currentStockEntity, 0.1) && MARules.MA5AboveMA20(currentStockEntity, 0) && (MARules.PriceBelowMA5(currentStockEntity, belowMA5Delta) || MARules.PriceBelowMA20(currentStockEntity, 0)); bool sc3 = MARules.PriceBelowMA20(currentStockEntity, 0); bool sc4 = !MARules.MA5AboveMA20(currentStockEntity, 0.1) && PriceRules.PriceBelowLast5IfCloseToMA5(currentStockEntity, sortedAllStockEntities, -0.02, 0.02) && PriceRules.PriceAboveLastBought(currentStockEntity, lastBoughtStockEntity, 0); bool sc5 = MARules.PriceBelowMA5(currentStockEntity, 0) && PriceRules.PriceBelowLastBought(currentStockEntity, lastBoughtStockEntity, 0.1); bool rtn = (sc1 || sc2 || sc3 || sc4 || sc5); if (rtn) { Console.WriteLine("{0}: Sell condition met: {1},{2},{3},{4},{5}", currentStockEntity.Date.ToString("yyyy-MM-dd"), sc1, sc2, sc3, sc4, sc5); } return rtn; }
public static bool MA20Rising(StockEntity stockEntity, List<StockEntity> sortedStockEntities, int continousCount, double increasingRate = 0) { int index = sortedStockEntities.IndexOf(stockEntity); double lastMA20 = -1; for (int i = Math.Max(0, index - continousCount + 1); i < index + 1; i++) { if (lastMA20 < 0) { lastMA20 = sortedStockEntities[i].GetMA(20); } else { if (sortedStockEntities[i].GetMA(20) < lastMA20 * (1 + increasingRate)) return false; else lastMA20 = sortedStockEntities[i].GetMA(20); } } return true; }
public static void CalculateRealTimeMA(string storageConnectionString, StockEntity stockEntity) { CloudStorageAccount storageAccount = CloudStorageAccount.Parse(storageConnectionString); CloudTableClient tableClient = storageAccount.CreateCloudTableClient(); tableClient.DefaultRequestOptions.RetryPolicy = new ExponentialRetry(TimeSpan.FromSeconds(30), 10); CloudTable table = tableClient.GetTableReference(Constants.StorageStockTableName); TableOperation retrieveStockEntityStatus = TableOperation.Retrieve<StockEntityStatus>("status-" + stockEntity.PartitionKey, "status"); var stockEntityStatus = (StockEntityStatus)table.Execute(retrieveStockEntityStatus).Result; if (stockEntityStatus == null || stockEntityStatus.LastestRawDataDate == DateTime.FromFileTimeUtc(0) || stockEntityStatus.GetLatestMAStartDate(stockEntityStatus.GetMaxAvailableMADataIndex()) == DateTime.FromFileTimeUtc(0)) { throw new InvalidOperationException("Please upload data and do initial processing first."); } TableOperation tableQuery = TableOperation.Retrieve<StockEntity>(stockEntity.PartitionKey, stockEntityStatus.LastestRawDataDate.ToString("yyyy-MM-dd")); var lastestClosedStockEntity = (StockEntity)table.Execute(tableQuery).Result; string pkFilter = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, stockEntity.PartitionKey); string rkLowerFilter = TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.GreaterThanOrEqual, stockEntityStatus.GetLatestMAStartDate(stockEntityStatus.GetMaxAvailableMADataIndex()).Subtract(TimeSpan.FromDays(10)).ToString("yyyy-MM-dd")); string rkHigherFilter = TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.LessThan, stockEntity.RowKey); string combinedFilter = TableQuery.CombineFilters(pkFilter, TableOperators.And, rkLowerFilter); combinedFilter = TableQuery.CombineFilters(combinedFilter, TableOperators.And, rkHigherFilter); TableQuery<StockEntity> query = new TableQuery<StockEntity>().Where(combinedFilter); var sortedStockEntities = table.ExecuteQuery<StockEntity>(query).OrderByDescending(entity => entity.Date).Take(stockEntityStatus.GetMaxAvailableMADataIndex() - 1).ToList(); double sum = stockEntity.Close; int[] MADataAvailable = stockEntityStatus.GetAvailableMADataIndex(); int MADataIndex = 0; for (int i = 0; i < sortedStockEntities.Count; i++) { sum += sortedStockEntities[i].Close; if (i == MADataAvailable[MADataIndex] - 2) { stockEntity.SetMA(MADataAvailable[MADataIndex], sum / MADataAvailable[MADataIndex]); MADataIndex++; } } if (sortedStockEntities.Count == (stockEntityStatus.GetMaxAvailableMADataIndex() - 1)) { stockEntity.SetMA(stockEntityStatus.GetMaxAvailableMADataIndex(), sum / stockEntityStatus.GetMaxAvailableMADataIndex()); } }
public static bool PriceBelowLast5IfCloseToMA5(StockEntity stockEntity, List<StockEntity> sortedStockEntities, double deltaForPrice, double deltaForMA5) { if (stockEntity.Close < stockEntity.GetMA(5) * (1 + deltaForMA5)) { int index = sortedStockEntities.IndexOf(stockEntity); double lastMinPrice = double.MaxValue; for (int i = Math.Max(0, index - 5); i < index; i++) { if (sortedStockEntities[i].Close <= lastMinPrice) lastMinPrice = sortedStockEntities[i].Close; } if (stockEntity.Close < lastMinPrice * (1 - deltaForPrice)) { return true; } } else { return false; } return false; }
public static bool PriceAboveLast5IfInShortPeriod(StockEntity stockEntity, StockEntity lastSoldStockEntity, List<StockEntity> sortedStockEntities, double delta) { if (lastSoldStockEntity == null) return true; if (stockEntity.Date.Subtract(lastSoldStockEntity.Date) <= TimeSpan.FromDays(7)) { int index = sortedStockEntities.IndexOf(stockEntity); double lastMaxPrice = 0; for (int i = Math.Max(0, index - 5); i < index; i++) { if (sortedStockEntities[i].Close >= lastMaxPrice) lastMaxPrice = sortedStockEntities[i].Close; } if (stockEntity.Close > lastMaxPrice * (1 + delta)) { return true; } } else { return true; } return false; }
public static bool PriceBelowLastBought(StockEntity stockEntity, StockEntity lastBoughtStockEntity, double delta) { if (lastBoughtStockEntity == null) return false; return stockEntity.Close < lastBoughtStockEntity.Close * (1 - delta); }
public static bool PriceAboveLastBought(StockEntity stockEntity, StockEntity lastBoughtStockEntity, double delta) { if (lastBoughtStockEntity == null) return true; return stockEntity.Close > lastBoughtStockEntity.Close * (1 + delta); }
private StockEntity GetTecentStockData(string stockCode) { if (azureToTecentApiMapping.ContainsKey(stockCode)) { string tecentApiStockCode = azureToTecentApiMapping[stockCode]; StockEntity stockEntity = new StockEntity(stockCode, DateTimeOffset.Parse(DateTime.Today.ToString("yyyy-MM-dd")).DateTime); string onlineData = string.Format(downloadTecentRealtimeStockDataApiStr, tecentApiStockCode); int retry = 0; while (retry < 10) { try { using (var client = new WebClient()) { Console.WriteLine("Getting data from {0}", onlineData); byte[] data = client.DownloadData(onlineData); string[] dataStr = Encoding.Default.GetString(data).Split(tecentRealtimeStockDataSplitter); stockEntity.Close = double.Parse(dataStr[indexOfPriceInTecentRealtimeStockData]); stockEntity.AdjClose = double.Parse(dataStr[indexOfPriceInTecentRealtimeStockData]); stockEntity.High = double.Parse(dataStr[indexOfHighPriceInTecentRealtimeStockData]); stockEntity.Low = double.Parse(dataStr[indexOfLowPriceInTecentRealtimeStockData]); stockEntity.Open = double.Parse(dataStr[indexOfOpenPriceInTecentRealtimeStockData]); stockEntity.Volume = double.Parse(dataStr[indexOfVolumeInTecentRealtimeStockData]) * 100; } break; } catch (Exception ex) { Console.WriteLine("Got exception when getting data: {0}", ex.ToString()); retry++; Thread.Sleep(TimeSpan.FromSeconds(retry)); } } return stockEntity; } else { throw new InvalidOperationException(string.Format("Cannot find tecent api code for stock: {0}", stockCode)); } }
public static bool PriceBelowMA20(StockEntity stockEntity, double delta) { return stockEntity.Close < stockEntity.GetMA(20) * (1 - delta); }
private StockEntity SellStock(StockEntity stockEntity, ref double money, ref double quantity) { Console.WriteLine("{0}: Sell at price {1}, MA5: {2}, MA20: {3}, Vol: {4}", stockEntity.Date.ToString("yyyy-MM-dd"), stockEntity.Close, stockEntity.GetMA(5), stockEntity.GetMA(20), stockEntity.Volume); money = quantity * stockEntity.Close; quantity = -1; return stockEntity; }
public static bool MA5AboveMA20(StockEntity stockEntity, double delta) { return stockEntity.GetMA(5) > stockEntity.GetMA(20) * (1 + delta); }
public bool BuyPolicy(StockEntity currentStockEntity, StockEntity lastBoughtStockEntity, StockEntity lastSoldStockEntity, List<StockEntity> sortedAllStockEntities) { bool bc1 = MARules.PriceAboveMA20(currentStockEntity, 0); bool bc2 = MARules.MA20Rising(currentStockEntity, sortedAllStockEntities, 5, 0.01); return (bc1 && bc2); }
public static bool VolumeBelowLastBought(StockEntity stockEntity, StockEntity lastBoughtStockEntity, double delta) { if (lastBoughtStockEntity == null) return true; return stockEntity.Volume < lastBoughtStockEntity.Volume * (1 - delta); }
public static bool PriveAboveButVolumeBelowLastBought(StockEntity stockEntity, StockEntity lastBoughtStockEntity, double delta) { if (lastBoughtStockEntity == null) return true; return stockEntity.Close >= lastBoughtStockEntity.Close && stockEntity.Volume < lastBoughtStockEntity.Volume * (1 - delta); }
public static bool PriceAboveMA20(StockEntity stockEntity, double delta) { return stockEntity.Close > stockEntity.GetMA(20) * (1 + delta); }
private List<StockEntity> ReadStockDataFromFileSource(string fileDataSource, string azureTableStockCode) { List<StockEntity> stockEntities = new List<StockEntity>(); Console.WriteLine("Parsing data from {0}", fileDataSource); using (StreamReader reader = new StreamReader(fileDataSource)) { string line = reader.ReadLine(); if (line != "Date,Open,High,Low,Close,Volume,Adj Close") { throw new InvalidOperationException("File data source format has been changed!"); } while (!reader.EndOfStream) { line = reader.ReadLine(); string[] splitters = line.Split(new char[] { ',' }); if (splitters.Length == 7) { try { DateTime date = DateTime.Parse(splitters[0]); double open = double.Parse(splitters[1]); double high = double.Parse(splitters[2]); double low = double.Parse(splitters[3]); double close = double.Parse(splitters[4]); double volume = double.Parse(splitters[5]); double adjClose = double.Parse(splitters[6]); var stockEntity = new StockEntity(azureTableStockCode, date) { Open = open, High = high, Low = low, Close = close, Volume = volume, AdjClose = adjClose }; stockEntities.Add(stockEntity); } catch { } } } } return stockEntities; }
public double CheckWinRate(string azureTableStockCode, ITradePolicy policy, string dateTimeSince = "1990-01-01", bool noDividend = false, bool saveResult = false) { CloudTable table = GetAzureTable(); string pkFilter = TableQuery.GenerateFilterCondition("PartitionKey", QueryComparisons.Equal, azureTableStockCode); string rkFilter = TableQuery.GenerateFilterCondition("RowKey", QueryComparisons.GreaterThanOrEqual, dateTimeSince); string combinedFilter = TableQuery.CombineFilters(pkFilter, TableOperators.And, rkFilter); TableQuery<StockEntity> query = new TableQuery<StockEntity>().Where(combinedFilter); var sortedStockEntities = table.ExecuteQuery<StockEntity>(query).OrderBy(entity => entity.Date).ToList(); double money = 1000; double quantity = -1; StockEntity lastBoughtStockEntity = new StockEntity(); StockEntity lastSoldStockEntity = new StockEntity(); StockEntity yesterdayStockEntity = null; long winCount = 0; long hugeWinCount = 0; long loseCount = 0; long hugeLoseCount = 0; foreach (var stockEntity in sortedStockEntities) { if (quantity < 0) { if (policy.BuyPolicy(stockEntity, lastBoughtStockEntity, lastSoldStockEntity, sortedStockEntities)) { lastBoughtStockEntity = BuyStock(stockEntity, ref money, ref quantity); } } else if (quantity > 0) { bool bonusDividend = false; double devidendRate = 1; if (!noDividend) { if (yesterdayStockEntity != null && stockEntity.Open > 0 && stockEntity.Open < yesterdayStockEntity.Close * 0.9) { Console.WriteLine("It's impossible that much lose happened, bonus dividend must have happened!"); bonusDividend = true; devidendRate = yesterdayStockEntity.Close / stockEntity.Open; quantity *= devidendRate; } } if (policy.SellPolicy(stockEntity, lastBoughtStockEntity, lastSoldStockEntity, sortedStockEntities)) { lastSoldStockEntity = SellStock(stockEntity, ref money, ref quantity); if (stockEntity.Close * devidendRate > lastBoughtStockEntity.Close) { Console.WriteLine("{0}: Win {1}%", stockEntity.Date.ToString("yyyy-MM-dd"), (stockEntity.Close * devidendRate - lastBoughtStockEntity.Close) / lastBoughtStockEntity.Close * 100); winCount++; if (stockEntity.Close * devidendRate > lastBoughtStockEntity.Close * 1.1) { hugeWinCount++; } } else { Console.WriteLine("{0}: Lose {1}%", stockEntity.Date.ToString("yyyy-MM-dd"), (lastBoughtStockEntity.Close - stockEntity.Close * devidendRate) / lastBoughtStockEntity.Close * 100); loseCount++; if (!bonusDividend && stockEntity.Close < lastBoughtStockEntity.Close * 0.9) { hugeLoseCount++; } } } } yesterdayStockEntity = stockEntity; } if (quantity > 0 && sortedStockEntities.LongCount() > 0) { money = sortedStockEntities.Last().Close * quantity; } Console.WriteLine("{4}: Money at the end is {0}, win count = {1}, huge win count = {5}, lose count = {2}, huge lose count = {6}, win rate = {3}%", money, winCount, loseCount, (double)winCount * 100 / (winCount + loseCount), azureTableStockCode, hugeWinCount, hugeLoseCount); if (saveResult) { TableOperation retrieveStockEntityStatus = TableOperation.Retrieve<StockEntityStatus>("status-" + azureTableStockCode, "status"); var stockEntityStatus = (StockEntityStatus)table.Execute(retrieveStockEntityStatus).Result; if (stockEntityStatus == null) { throw new InvalidOperationException("Please upload data and do initial processing first."); } if (dateTimeSince == "1990-01-01") { stockEntityStatus.OverallWinRate = (double)winCount * 100 / (winCount + loseCount); stockEntityStatus.OverallProfitRate = (money - 1000) * 100 / 1000; } else { stockEntityStatus.RecentWinRate = (double)winCount * 100 / (winCount + loseCount); stockEntityStatus.RecentProfitRate = (money - 1000) * 100 / 1000; } table.Execute(TableOperation.InsertOrMerge(stockEntityStatus)); } return (double)winCount * 100 / (winCount + loseCount); }