private async Task <bool> GetOrders(TradeTask tt) { foreach (var job in tt.FinishedJobs.Where(IsActiveJob)) { var result = await GetOrder(job); job.ExchangeOrder = result; result.Fills.ForEach(x => tt.RegisterTrade(x)); } if (tt.LastGetOrder.AddSeconds(5) <= DateTime.Now) { foreach (var job in tt.Jobs.Where(IsActiveJob)) { var result = await GetOrder(job); if (job.ExchangeOrder == null || result.Updated > job.ExchangeOrder?.Updated) { job.ExchangeOrder = result; tt.Updated = DateTime.Now; TradeTaskViewModel.SerializeModel(tt); var ttvm = TradeTasksList.SingleOrDefault(x => x.Model == tt); ttvm.Status = BuildStatusString(tt, job); result.Fills.ForEach(x => tt.RegisterTrade(x)); } tt.LastGetOrder = DateTime.Now; } } return(true); }
private async Task DoLifecycleImpl(TradeTask task) { if (task == null || task.IsBusy) { return; } try { await task.locker.WaitAsync(); var pt = GetPriceTicker(task.Symbol); var result = await GetOrders(task); if (task.Status == TradeTaskStatus.Running) { if (result) { await Lifecycle(task, pt); } } else { var ttvm = TradeTasksList.SingleOrDefault(x => x.Model == task); ttvm.Status = BuildStatusString(task.Status); } } catch (ApiException ex) { var ttvm = TradeTasksList.SingleOrDefault(x => x.Model == task); if (ttvm != null) { ttvm.LastError = ex.Error; } } catch (Exception) { } finally { task.locker.Release(); } }
// before running the lifecycle, update ALL task orders // with status = ACTIVE | PARTIALLY_FILLED. private async Task Lifecycle(TradeTask tt, PriceTicker ticker) { if (!tt.Jobs.Any()) { // Shutdown the task. tt.Status = TradeTaskStatus.Finished; tt.Updated = DateTime.Now; tt.Events.Add(tt.Updated, "Task finished."); TradeTaskViewModel.SerializeModel(tt); return; } bool isLimitOrdersAllowed = true; // NOTE: this routine relies on condition that // LIMIT orders are always on TOP of MARKET orders in list of jobs. foreach (var job in tt.Jobs.ToList()) { if (job.ExchangeOrder != null) { switch (job.ExchangeOrder.Status) { case OrderStatus.Cancelled: tt.Status = TradeTaskStatus.Stopped; tt.Updated = DateTime.Now; tt.Events.Add(tt.Updated, "Task stopped due to order was cancelled outside."); TradeTaskViewModel.SerializeModel(tt); return; case OrderStatus.Filled: if (job.Kind == OrderKind.StopLoss) { // Shutdown the task. tt.Status = TradeTaskStatus.Finished; tt.Updated = DateTime.Now; tt.Events.Add(tt.Updated, "Task finished by stop loss."); } else if (job.Kind == OrderKind.PanicSell) { tt.Status = TradeTaskStatus.PanicSell; tt.Updated = DateTime.Now; tt.Events.Add(tt.Updated, "Task stopped by panic sell."); } tt.FinishedJobs.Enqueue(job); tt.Jobs.Remove(job); TradeTaskViewModel.SerializeModel(tt); return; case OrderStatus.Active: case OrderStatus.PartiallyFilled: // THIS STATE IS ONLY POSSIBLE FOR LIMIT ORDERS OR PANIC SELL! // so allow ONLY MARKET order to check conditions and execute isLimitOrdersAllowed = false; break; } } else { bool applicable = false; bool isLimitOrder = (job.Type == OrderType.LIMIT || job.Type == OrderType.STOP_LIMIT); // check condition if (job.Kind == OrderKind.Buy) { applicable = (tt.StopLoss.Price < ticker.Ask) && (ticker.Ask < tt.TakeProfit.First().Price); if (!applicable) { var ttvm = TradeTasksList.SingleOrDefault(x => x.Model == tt); ttvm.Status = "Цена вне зоны покупки"; } } else if (job.Kind == OrderKind.PanicSell) { applicable = true; } else { applicable = (job.Type != OrderType.MARKET && job.Type != OrderType.TRAILING) || (ticker.Bid >= job.Price); // NOTE: last part is wrong -- could be market STOP_LOSS, so ticker.Bid <= job.Price is valid for this case } // !!!ALLOW ONLY 1 LIMIT ORDER AT A TIME FOR NOW!!! if (applicable && (isLimitOrdersAllowed || !isLimitOrder) && (tt.Qty > 0 || job.Side == TradeSide.Buy)) { var result = await CancellAllOrders(tt); if (job.Side == TradeSide.Sell) { // Do not sell more then you have :D job.Quantity = Math.Min(tt.Qty, job.Quantity); } job.ExchangeOrder = await ExecuteOrder(job); job.OrderId = job.ExchangeOrder.OrderId; tt.Updated = DateTime.Now; tt.Events.Add(tt.Updated, $"Created order {job.OrderId}."); TradeTaskViewModel.SerializeModel(tt); job.ExchangeOrder.Fills.ForEach(x => tt.RegisterTrade(x)); var ttvm = TradeTasksList.SingleOrDefault(x => x.Model == tt); ttvm.Status = BuildStatusString(tt, job); } return; // exit for } } }