Ejemplo n.º 1
0
 private SymbolData GetOrAddData(ISymbolInfo symbol)
 {
     if (!SymbolsData.TryGetValue(symbol.Key, out SymbolData data))
     {
         data = new SymbolData(symbol);
         _SymbolsData[symbol.Key] = data;
     }
     return(data);
 }
Ejemplo n.º 2
0
        private SymbolData GetSymbolData(SymbolInfo sym)
        {
            SymbolData symbolData;

            if (!_SymbolsData.TryGetValue(sym.Key, out symbolData))
            {
                symbolData = new SymbolData(sym);
                _SymbolsData.Add(sym.Key, symbolData);
            }
            return(symbolData);
        }
Ejemplo n.º 3
0
        private async Task ManageSymbol(SymbolData symData, TimeSlice slice)
        {
            var symbol = symData.Feed.Symbol;

            //foreach operation check if the entry is enough
            foreach (Operation operation in symData.ActiveOperations)
            {
                MyOperationData execData = InitializeOperationData(operation);

                //check if it needs to be closed
                if (operation.IsEntryExpired(Algo.Time) && (operation.AmountInvested <= 0 || operation.AmountRemaining / operation.AmountInvested <= 0.03m))
                {
                    operation.ScheduleClose(Algo.Time.AddSeconds(5));
                    continue;
                }

                //check if entry needed
                if (operation.AmountInvested <= 0 && !operation.IsEntryExpired(Algo.Time) && !Algo.EntriesSuspended)
                {
                    var gotEntry = operation.Type == OperationType.BuyThenSell ?
                                   (decimal)symData.Feed.Ask <= operation.Signal.PriceEntry :
                                   (decimal)symData.Feed.Bid >= operation.Signal.PriceEntry;
                    //todo  check if the last entry order was effectively executed
                    //check if we have already sent an order
                    if (execData.LastEntryOrder == null)
                    {
                        var originalAmount = AssetAmount.Convert(operation.AmountTarget, operation.Symbol.Asset, symData.Feed);
                        var stillToBuy     = originalAmount - operation.AmountInvested;
                        if (stillToBuy / originalAmount > 0.2m)
                        {
                            //TODO check requirements for order: price precision, minimum amount, min notional, available money
                            var orderInfo = new OrderInfo()
                            {
                                Symbol        = symbol.Key,
                                Type          = OrderType.Market,
                                Effect        = Algo.DoMarginTrading ? MarginOrderEffect.OpenPosition : MarginOrderEffect.None,
                                Amount        = stillToBuy,
                                ClientOrderId = operation.GetNewOrderId(),
                                Direction     = execData.EntryDirection
                            };
                            var ticket = await Algo.Market.PostNewOrder(orderInfo);

                            if (ticket.Status == RequestStatus.Completed)
                            {
                                //Market order was executed - we should expect the trade on next update
                                execData.LastEntryOrder = ticket.Result;
                            }
                            else
                            {
                                //todo log error
                            }
                        }
                    }
                }

                //check if exit needed
                if (execData.LastExitOrder == null)
                {
                    var shouldExit = operation.Signal.ExpireDate < Algo.Time;
                    var gotTarget  = operation.Type == OperationType.BuyThenSell ?
                                     (decimal)symData.Feed.Bid >= operation.Signal.PriceTarget :
                                     (decimal)symData.Feed.Ask <= operation.Signal.PriceTarget;

                    if (shouldExit || gotTarget)
                    {
                        var amount = operation.AmountRemaining;
                        //TODO check requirements for order: price precision, minimum amount, min notional, available money
                        var orderInfo = new OrderInfo()
                        {
                            Symbol        = symbol.Key,
                            Type          = OrderType.Market,
                            Effect        = Algo.DoMarginTrading ? MarginOrderEffect.ClosePosition : MarginOrderEffect.None,
                            Amount        = amount,
                            ClientOrderId = operation.GetNewOrderId(),
                            Direction     = execData.ExitDirection
                        };
                        var ticket = await Algo.Market.PostNewOrder(orderInfo);

                        if (ticket.Status == RequestStatus.Completed)
                        {
                            //Market order was executed - we should expect the trade on next update
                            execData.LastExitOrder = ticket.Result;
                        }
                        else
                        {
                            //TODO log error
                        }
                    }
                }
            }
        }
Ejemplo n.º 4
0
        public async Task Update(TimeSlice slice)
        {
            LastUpdate = Market.Time;
            //update selected symbols
            var changes = await SymbolsFilter.UpdateAsync(slice);

            if (changes != SelectedSymbolsChanges.None)
            {
                var selectedForOperationsActive = this.ActiveOperations.Select(ao => ao.Symbol).GroupBy(ao => ao.Key).Select(g => g.First()).ToList();
                //release feeds of unused symbols
                foreach (var sym in changes.RemovedSymbols)
                {
                    SymbolData symbolData = GetSymbolData(sym);
                    symbolData.IsSelectedForTrading = false;
                    //if it doesn't have acrive operations
                    if (!selectedForOperationsActive.Any(aos => aos.Key == sym.Key))
                    {
                        if (symbolData.Feed != null)
                        {
                            symbolData.Feed.OnData -= Feed_OnData;
                            this.ReleaseFeed(symbolData.Feed);
                            symbolData.Feed = null;
                        }
                    }
                }

                //add feeds for added symbols and those that have open operations
                foreach (var sym in changes.AddedSymbols)
                {
                    SymbolData symbolData = GetSymbolData(sym);
                    symbolData.IsSelectedForTrading = true;
                }

                foreach (var sym in changes.AddedSymbols.Concat(selectedForOperationsActive))
                {
                    SymbolData symbolData = GetSymbolData(sym);
                    if (symbolData.Feed == null)
                    {
                        symbolData.Feed = await this.GetSymbolFeed(sym.Key);

                        symbolData.Feed.OnData -= Feed_OnData;
                        symbolData.Feed.OnData += Feed_OnData;
                    }
                }

                if (Sentry != null)
                {
                    await Sentry.OnSymbolsChanged(changes);
                }

                if (Allocator != null)
                {
                    Allocator.OnSymbolsChanged(changes);
                }

                if (Executor != null)
                {
                    Executor.OnSymbolsChanged(changes);
                }

                if (RiskManager != null)
                {
                    RiskManager.OnSymbolsChanged(changes);
                }
            }

            // register trades with their linked operations
            Operation[]      oldOperations     = null;
            List <Operation> operationsResumed = new List <Operation>();

            foreach (ITrade trade in slice.Trades)
            {
                //first search in active operations
                var symData  = _SymbolsData[trade.Symbol];
                var activeOp = symData.ActiveOperations.FirstOrDefault(op => op.IsTradeAssociated(trade));
                if (activeOp != null)
                {
                    if (activeOp.AddTrade(trade))
                    {
                        Logger.Info($"{Time} - New trade for operation {activeOp.ToString()}: {trade.ToString()}");
                    }
                }
                else
                {
                    //let's search in closed operations by getting ligtweigth instances
                    if (oldOperations == null)
                    {
                        //lock (DbLock)
                        //    oldOperations = oldOperations ?? DbClosedOperations.Find(o => o.CreationTime >= Market.Time.AddDays(-5)).ToArray();
                        lock (DbLock)
                            oldOperations = QueryClosedOperations(doc => doc["CreationTime"].AsDateTime >= Market.Time.AddDays(-4));
                    }

                    var oldOp = oldOperations.FirstOrDefault(op => op.IsTradeAssociated(trade));
                    //if we found an operation let's get a real instance
                    if (oldOp != null)
                    {
                        oldOp = DbClosedOperations.FindById(oldOp.Id);
                        if (oldOp == null)
                        {
                            Logger.Error($"Operation found then not found!  {oldOp}.");
                        }
                    }

                    if (oldOp != null)
                    {
                        operationsResumed.Add(oldOp);
                        if (oldOp.AddTrade(trade))
                        {
                            Logger.Info($"{Time} - New trade for 'old' operation {activeOp}: {trade.ToString()}");
                        }
                        //check if it got resumed by this new trade
                        if (!oldOp.IsClosed)
                        {
                            this.ResumeOperation(oldOp);
                            Logger.Info($"Resuming 'old' operation {activeOp}.");
                        }
                        else
                        {
                            oldOp.Dispose(); //otherwise we must dispose it
                        }
                    }
                    else
                    {
                        Logger.Debug($"{Time} - New trade {trade.ToString()} without any associated operation");
                    }
                }
            }
            //dispose the operations that we didn't use
            if (oldOperations != null)
            {
                foreach (var oper in oldOperations.Where(oo => !operationsResumed.Contains(oo)))
                {
                    oper.Dispose();
                }
            }

            // call OnUpdate
            await OnUpdate(slice);

            // get signals
            if (Sentry != null)
            {
                Sentry.UpdateAsync(slice);
            }

            //create operations
            if (Allocator != null)
            {
                Allocator.Update(slice);
            }

            //close operations that have been in close queue for enough time
            lock (DbLock)
            {
                this.Db?.BeginTrans();
                List <Operation> operationsToClose = _ActiveOperations.Where(op => this.Time >= op.CloseDeadTime).ToList();
                foreach (var op in operationsToClose)
                {
                    if (op.AmountInvested > 0)
                    {
                        Logger.Info("{0} - Closing operation {1}.", Time, op.ToString("c"));
                    }
                    else
                    {
                        Logger.Debug("{0} - Closing operation {1}.", Time, op.ToString("c"));
                    }
                    op.Close();
                    _ActiveOperations.Remove(op);
                    if (this.Config.SaveData || op.AmountInvested > 0)
                    {
                        this._ClosedOperations.Add(op);
                    }
                    this.SymbolsData[op.Symbol.Key].CloseOperation(op);

                    if (this.Config.SaveData)
                    {
                        lock (DbLock)
                        {
                            //update database
                            DbActiveOperations.Delete(op.Id);
                            DbClosedOperations.Upsert(op);
                        }
                    }
                }
                this.Db?.Commit();
            }

            //add new operations that have been created
            foreach (var op in slice.NewOperations)
            {
                AddActiveOperation(op);
            }

            //manage orders
            if (Executor != null)
            {
                await Executor.Update(slice);
            }

            //manage risk
            if (RiskManager != null)
            {
                await RiskManager.Update(slice);
            }

            while (Commands.Count > 0)
            {
                try
                {
                    if (Commands.TryDequeue(out Command command))
                    {
                        await command.Run();
                    }
                }
                catch (Exception ex)
                {
                    Logger.Error($"Error while running command: {ex.Message}.");
                }
            }

            if (Config.SaveData)
            {
                SaveNonVolatileVars();
            }
        }
Ejemplo n.º 5
0
 public (decimal price, decimal amount) ClampOrderAmount(SymbolData symData, TradeDirection tradeDirection, (decimal price, decimal amount) adj)