static void PrepareSellForMore(List <KLineData> coinInfos, TradeItem tradeItem) { string quote = tradeItem.quote; string symbol = tradeItem.symbol; // 判断是否交易。 var needSellOldData = new BuyInfoDao().ListNeedSellOrder(quote, symbol); foreach (var item in needSellOldData) { if (item.BuyStatus != "filled") { continue; } // 这里的策略真的很重要 // 如果超过20%, 则不需要考虑站稳, 只要有一丁点回调就可以 // 如果超过7%, 站稳则需要等待3个小时 if (coinInfos[0].close < item.BuyPrice * (decimal)1.09) { continue; } // 找到最大的close var maxClose = coinInfos.Max(it => it.close); var percent = coinInfos[0].close / item.BuyPrice; // 现在的价格/购买的价格 var huidiaoPercent = 1 + (percent - 1) / 10; if (coinInfos[0].close * (decimal)1.03 < maxClose || coinInfos[0].close * huidiaoPercent < maxClose) { Console.WriteLine($"PrepareSell --> {item.Quote}--{item.Symbol}----"); // 出售, 适当的回调,可以出售 DoSellForMore(item, coinInfos[0].close); } } }
public static decimal GetAvgPrice(TradeItem item, string granularity) { var klineDataList = OkApi.GetKLineDataAsync(item.symbol + "-" + item.quote, granularity); if (klineDataList == null) { return(0); } var count = 0; var totalPrice = (decimal)0; foreach (var data in klineDataList) { var dataItemPrice = (data.open + data.close) / 2; totalPrice += dataItemPrice; count++; } // var avgPrice = totalPrice / count; avgPrice = decimal.Round(avgPrice, 8); return(avgPrice); }
public static void Check(TradeItem tradeItem) { try { DoCheck(tradeItem); } catch (Exception ex) { logger.Error(ex.Message, ex); } }
static void RunTrade(List <KLineData> coinInfos, TradeItem tradeItem) { // 做多购买 PrepareBuyForMore(coinInfos, tradeItem); // 多单收割 PrepareSellForMore(coinInfos, tradeItem); // 做空出售 PrepareSellForEmpty(coinInfos, tradeItem); // 做空购买 PrepareBuyForEmpty(coinInfos, tradeItem); }
static void PrepareSellForEmpty(List <KLineData> coinInfos, TradeItem tradeItem) { if (tradeItem.SmallSellPrice <= 0 || tradeItem.EmptySize <= 0) { return; } string quote = tradeItem.quote; string symbol = tradeItem.symbol; decimal nowPrice = coinInfos[0].close; // 读取数据库 看看以前的交易 var oldData = new SellInfoDao().List5HigherSellForEmpty(quote, symbol); // 判断是否阶梯 var bigTheSellPrice = false; if (oldData.Count > 0) { var rateDecimal = (decimal)1.088; bigTheSellPrice = nowPrice > oldData[0].SellPrice * rateDecimal; if (oldData[0].SellTradePrice > 0 && oldData[0].SellTradePrice >= oldData[0].SellPrice) { bigTheSellPrice = nowPrice > oldData[0].SellTradePrice * rateDecimal; } } if (oldData.Count == 0 || bigTheSellPrice) { // coinfInfos的最高价和最低价相差不能太大 var min = coinInfos.Min(it => it.low); var max = coinInfos.Max(it => it.high); if (tradeItem.SmallSellPrice > 0 && nowPrice < tradeItem.SmallSellPrice) { // logger.Error($"价格过低,不能售出 quote: {quote}, symbol:{symbol}"); return; } // 是否超过了最小售价 if (InstrumentsUtils.CheckSmallSellPrice(quote, symbol, coinInfos[0].close)) { // 出售一单 Console.WriteLine($"PrepareSellForEmpty --> {quote}, {symbol}"); if (oldData.Count > 0) { logger.Error($"相差间隔 lastPrice: {oldData[0].Id} -- {oldData[0].SellTradePrice}, nowPrice:{nowPrice}, rate: {(oldData[0].SellTradePrice == 0 ? 0 : (nowPrice / oldData[0].SellTradePrice))} -- { oldData[0].SellTradePrice / nowPrice}"); } DoSellForEmpty(tradeItem, nowPrice); return; } } }
static void PrepareBuyForEmpty(List <KLineData> coinInfos, TradeItem tradeItem) { string quote = tradeItem.quote; string symbol = tradeItem.symbol; // 判断是否交易。 var needBuyOldData = new SellInfoDao().ListNeedBuyOrder(quote, symbol); if (needBuyOldData.Count > 0) { Console.WriteLine($"----------> {quote} {symbol} 空: {needBuyOldData.Count}"); } foreach (var item in needBuyOldData) { if (item.SellStatus != "filled") { continue; } // 这里的策略真的很重要 // 如果超过20%, 则不需要考虑站稳, 只要有一丁点回调就可以 // 如果超过7%, 站稳则需要等待3个小时 if (coinInfos[0].close * (decimal)1.09 > item.SellPrice) { continue; } // 找到最大的close var minClose = coinInfos.Min(it => it.close); var percent = item.SellPrice / coinInfos[0].close; // 现在的价格/出售的价格 var huidiaoPercent = 1 + (percent - 1) / 10; if (coinInfos[0].close > minClose * (decimal)1.03 || coinInfos[0].close > minClose * huidiaoPercent) { Console.WriteLine($"PrepareBuyForEmpty --> {item.Quote}--{item.Symbol}----"); // 出售, 适当的回调,可以购入 DoBuyForEmpty(item, coinInfos[0].close); } } }
static void DoSellForEmpty(TradeItem tradeItem, decimal nowPrice) { string quote = tradeItem.quote; string symbol = tradeItem.symbol; if (lastSellForEmptyDate > DateTime.Now.AddSeconds(-20)) { // 如果20秒内做空过一单, 则不能再次做空 return; } var sellSize = tradeItem.EmptySize; if (sellSize <= 0) { return; } var count = new SellInfoDao().GetNotBuyCount(quote, symbol); count = Math.Min(count, 60); sellSize = sellSize * (1 + count / (decimal)30); var sellPrice = nowPrice / (decimal)1.01; var okInstrument = InstrumentsUtils.GetOkInstruments(quote, symbol); sellPrice = decimal.Round(sellPrice, okInstrument.GetTickSizeNumber()); sellSize = decimal.Round(sellSize, okInstrument.GetSizeIncrementNumber()); var client_oid = "sell" + DateTime.Now.Ticks; try { logger.Error($""); logger.Error($"1: 准备出售(空) {quote}-{symbol}, client_oid:{client_oid}, nowPrice:{nowPrice}, sellPrice:{sellPrice.ToString()}, sellSize:{sellSize.ToString()}"); var tradeResult = OkApi.Sell(client_oid, symbol + "-" + quote, sellPrice.ToString(), sellSize.ToString()); logger.Error($"2: 下单完成 {JsonConvert.SerializeObject(tradeResult)}"); new SellInfoDao().CreateSellInfo(new SellInfo { SellClientOid = client_oid, SellPrice = sellPrice, SellQuantity = sellSize, SellCreateAt = DateTime.Now.ToString("yyyy-MM-dd"), SellFilledNotional = (decimal)0, SellStatus = "prepare", SellOrderId = tradeResult.order_id, SellResult = tradeResult.result, Quote = quote, Symbol = symbol, UserName = "******" }); logger.Error($"3: 添加记录完成"); logger.Error($""); lastSellForEmptyDate = DateTime.Now; } catch (Exception e) { logger.Error("做空出售异常 严重 --> " + e.Message, e); Thread.Sleep(1000 * 60 * 10); } }
static void PrepareBuyForMore(List <KLineData> coinInfos, TradeItem tradeItem) { string quote = tradeItem.quote; string symbol = tradeItem.symbol; decimal nowPrice = coinInfos[0].close; // 读取数据库 看看以前的交易 var oldData = new BuyInfoDao().List5LowerBuyForBuy(quote, symbol); if (tradeItem.symbol.ToLower() == "egt" && tradeItem.quote.ToLower() == "okb") { logger.Error($"egt 数量: {oldData.Count}, {new DateTime(2019, 6, 12)}"); oldData.RemoveAll(it => DateUtils.GetDate(it.BuyCreateAt) < new DateTime(2019, 6, 12)); logger.Error($"egt 剩余数量: {oldData.Count}"); } // 判断是否阶梯 var smallThenBuyPrice = false; if (oldData.Count > 0) { var rateDecimal = (decimal)1.078; if (tradeItem.quote.ToLower() == "okb") { rateDecimal = (decimal)1.05; } if (tradeItem.BuyLadderRatio > (decimal)1.066) { rateDecimal = tradeItem.BuyLadderRatio; } smallThenBuyPrice = nowPrice * rateDecimal < oldData[0].BuyPrice; if (oldData[0].BuyTradePrice > 0 && oldData[0].BuyTradePrice <= oldData[0].BuyPrice) { smallThenBuyPrice = nowPrice * rateDecimal < oldData[0].BuyTradePrice; } } if (oldData.Count == 0 || smallThenBuyPrice) { // coinfInfos的最高价和最低价相差不能太大 var min = coinInfos.Min(it => it.low); var max = coinInfos.Max(it => it.high); if (tradeItem.MaxBuyPrice > 0 && nowPrice > tradeItem.MaxBuyPrice) { logger.Error($"价格过高,不能购入 quote: {quote}, symbol:{symbol}"); } else { // 是否超过了最大限价 if (max < 2 * min && InstrumentsUtils.CheckMaxBuyPrice(quote, symbol, coinInfos[0].close)) { // 购买一单 Console.WriteLine($"PrepareBuy --> {quote}, {symbol}"); if (oldData.Count > 0) { logger.Error($"相差间隔 lastPrice: {oldData[0].Id} -- {oldData[0].BuyTradePrice}, nowPrice:{nowPrice}, rate: {(oldData[0].BuyTradePrice == 0 ? 0 : (nowPrice / oldData[0].BuyTradePrice))} -- { oldData[0].BuyTradePrice / nowPrice}"); } DoBuyForMore(quote, symbol, nowPrice); return; } } } }
public static void DoCheck(TradeItem tradeItem) { var instrument = $"{tradeItem.symbol}-{tradeItem.quote}".ToUpper(); // 核实下 最近 10个小时是否统计过, if (lastCheckDate > DateTime.Now.AddMinutes(-1)) { // 最近1分钟有核实过, 则不再核实 return; } if (lastCheckDateDic.ContainsKey(instrument) && lastCheckDateDic[instrument] > DateTime.Now.AddHours(-12)) { // 这个交易对, 12个小时内核实过,则不再核实 return; } else { if (lastCheckDateDic.ContainsKey(instrument)) { lastCheckDateDic[instrument] = DateTime.Now; } else { lastCheckDateDic.Add(instrument, DateTime.Now); } } lastCheckDate = DateTime.Now; var orderList = OkApi.ListFilledOrder(instrument); Console.WriteLine($"需要核实的订单数据:{orderList.Count}"); foreach (var order in orderList) { if (order.side == "buy") { // 列出数据库里面的交易记录, 对比一下。 var orderInDb = new BuyInfoDao().GetBuyOrder(order.client_oid); var orderInDb2 = new SellInfoDao().GetBuyOrder(order.client_oid); if (( orderInDb == null || orderInDb.BuyClientOid != order.client_oid || orderInDb.BuyCreateAt != order.created_at || orderInDb.BuyStatus != order.status || orderInDb.BuyOrderId != order.order_id ) && ( orderInDb2 == null || orderInDb2.BuyClientOid != order.client_oid || orderInDb2.BuyCreateAt != order.created_at || orderInDb2.BuyStatus != order.status || orderInDb2.BuyOrderId != order.order_id )) { logger.Error($"有个订单不合理,快速查看一下 begin"); logger.Error($"{JsonConvert.SerializeObject(order)}"); logger.Error($"{JsonConvert.SerializeObject(orderInDb)}"); logger.Error($"{JsonConvert.SerializeObject(orderInDb2)}"); logger.Error($"有个订单不合理,快速查看一下 end "); } } else if (order.side == "sell") { // 列出数据库里面的交易记录, 对比一下。 var orderInDb = new BuyInfoDao().GetSellOrder(order.client_oid); var orderInDb2 = new SellInfoDao().GetSellOrder(order.client_oid); if (( orderInDb == null || orderInDb.SellClientOid != order.client_oid || orderInDb.SellCreateAt != order.created_at || orderInDb.SellStatus != order.status || orderInDb.SellOrderId != order.order_id ) && ( orderInDb2 == null || orderInDb2.SellClientOid != order.client_oid || orderInDb2.SellCreateAt != order.created_at || orderInDb2.SellStatus != order.status || orderInDb2.SellOrderId != order.order_id )) { logger.Error($"有个订单不合理,快速查看一下 begin"); logger.Error($"{JsonConvert.SerializeObject(order)}"); logger.Error($"{JsonConvert.SerializeObject(orderInDb)}"); logger.Error($"{JsonConvert.SerializeObject(orderInDb2)}"); logger.Error($"有个订单不合理,快速查看一下 end "); } } else { logger.Error($"数据统计不正确, 不存在的side"); Console.WriteLine($"{JsonConvert.SerializeObject(order)}"); } } }