public async Task <TradeActionResponse> ExecuteAction(bool simulate) { // Decrement from account, increment to account. this.FromAccount.Balance -= this.EstimatedCost + this.TransactionAmount; this.ToAccount.Balance += this.TransactionAmount; var resp = new TradeActionResponse() { ExecutionSuccessful = true, Transactions = null }; using (var ctx = new RBBotContext()) { ctx.Entry(this.FromAccount).State = System.Data.Entity.EntityState.Modified; ctx.Entry(this.ToAccount).State = System.Data.Entity.EntityState.Modified; await ctx.SaveChangesAsync(); if (!simulate) { #warning Still to implement the actual thing!! } throw new NotImplementedException(); } return(resp); }
public static void LoadSystemSettings(RBBotContext dbContext) { // string preferredCode = SettingHelper.GetSystemSetting("PreferedCryptoCurrency"); PreferredCyptoCurrency = dbContext.Currencies.Where(x => x.Code == preferredCode).Single(); MinimumTradeOpportunityPercent = Convert.ToDecimal(SettingHelper.GetSystemSetting("MinimumTradeOpportunityPercent")); OpportunityTimeoutInSeconds = Convert.ToInt32(SettingHelper.GetSystemSetting("OpportunityTimeoutInSeconds")); ExchangeConnectionTimeoutInSeconds = Convert.ToInt32(SettingHelper.GetSystemSetting("ExchangeConnectionTimeoutInSeconds")); }
/// <summary> /// On market price change this is called to persist the information to db. /// </summary> /// <param name="change"></param> public async Task OnMarketPriceChangeAsync(PriceChangeEvent change) { // Save to database. using (var ctx = new RBBotContext()) { ctx.MarketPrices.Add(new Models.MarketPrice() { ExchangeTradePairId = change.ExchangeTradePair.Id, Price = change.Price, Timestamp = change.UtcTime }); // Write to console. //Console.WriteLine($"Price Change on {change.ExchangeTradePair.Exchange.Name} for {change.ExchangeTradePair.TradePair.FromCurrency.Code} - {change.ExchangeTradePair.TradePair.ToCurrency.Code}. New Price: {change.Price}"); await ctx.SaveChangesAsync(); } }
public async Task ExecuteAction(bool simulate) { // Decrement from account, increment to account. this.FromAccount.Balance -= this.EstimatedCost + this.TransferAmount; this.ToAccount.Balance += this.TransferAmount; using (var ctx = new RBBotContext()) { ctx.Entry(this.FromAccount).State = System.Data.Entity.EntityState.Modified; ctx.Entry(this.ToAccount).State = System.Data.Entity.EntityState.Modified; await ctx.SaveChangesAsync(); } if (!simulate) { #warning Still to implement the actual thing!! } }
public CycleFinder(Exchange exchange) { this.exchange = exchange; currencyDict = new Dictionary <int, Currency>(); using (var ctx = new RBBotContext()) { currencyDict = ctx.Currencies.ToDictionary(x => x.Id, y => y); } // Get an array of the trade pairs in a jagged array graph = new int[exchange.ExchangeTradePairs.Count, 2]; for (int i = 0; i < exchange.ExchangeTradePairs.Count; i++) { graph[i, 0] = exchange.ExchangeTradePairs.ElementAt(i).TradePair.FromCurrencyId; graph[i, 1] = exchange.ExchangeTradePairs.ElementAt(i).TradePair.ToCurrencyId; } }
/// <summary> /// On market price change this is called to persist the information to db. /// </summary> /// <param name="change"></param> public async Task <IEnumerable <Opportunity> > OnMarketPriceChangeAsync(ExchangeTradePair change) { // Save to database. using (var ctx = new RBBotContext()) { ctx.MarketPrices.Add(new Models.MarketPrice() { ExchangeTradePairId = change.Id, Price = change.LatestPrice, Timestamp = change.LatestUpdate }); // Write to console. //Console.WriteLine($"Price Change on {change.ExchangeTradePair.Exchange.Name} for {change.ExchangeTradePair.TradePair.FromCurrency.Code} - {change.ExchangeTradePair.TradePair.ToCurrency.Code}. New Price: {change.Price}"); await ctx.SaveChangesAsync(); // Always return null! There's no opportunity in the observer. return(new Opportunity[] { }); } }
/// <summary> /// This method is used to actually execute the opportunity /// </summary> /// <param name="simulate"></param> public async Task ExecuteOpportunity(bool simulate = true) { // Loop through all actions. foreach (var syncActions in this.Actions) { // Wait all tasks to finish, but launch in parallel! Task.WaitAll(syncActions.Select(x => x.ExecuteAction(simulate)).ToArray()); } using (var ctx = new RBBotContext()) { if (this.OpportunityModel != null) { return; // Should never be the case, but never know. } #warning this is incomplete!! lock (this.OpportunityModel) { } } }
private static async Task <TradeOpportunity> GetOrUpdateTradeOpportunity(Opportunity opportunity, bool IsSimulation) { // Update requirements and maximum possible trade. var requirements = opportunity.UpdateRequirementsAndAmount().ToList(); // string stateCode = opportunity.RequirementsMet ? "RQ-MET" : "RQ-MISSING"; decimal oppValue = opportunity.GetMarginValuePercent(); // Create the trade value; var newTradeValue = new TradeOpportunityValue() { PotentialMargin = oppValue, Timestamp = DateTime.UtcNow, TradeOpportunityStateId = TradeOpportunityState.States.Single(x => x.Code == stateCode).Id }; // If the oppvalue is below treshold and doesn't exist yet, then ignore it. Else stop it. if (oppValue < SystemSetting.MinimumTradeOpportunityPercent) { TradeOpportunity tradOpp = null; tradeOpportunities.TryGetValue(opportunity.UniqueIdentifier, out tradOpp); if (tradOpp == null) { return(null); } else { var belowThresholdState = TradeOpportunityState.States.Single(x => x.Code == "EXP-BELOW"); newTradeValue.TradeOpportunityStateId = belowThresholdState.Id; await EndTradeOpportunity(tradOpp, belowThresholdState, newTradeValue); return(null); } } // Try getting the TradeOpp from the dictionary. If it doesn't exist, then construct it now var isNewOpportunity = false; var tradeOpp = tradeOpportunities.GetOrAdd(opportunity.UniqueIdentifier, (key) => { isNewOpportunity = true; return(new TradeOpportunity() { CurrencyId = opportunity.OpportunityBaseCurrency.Id, IsExecuted = false, IsSimulation = IsSimulation, StartTime = DateTime.UtcNow, TradeOpportunityTypeId = TradeOpportunityType.Types.Single(x => x.Code == opportunity.TypeCode).Id, TradeOpportunityStateId = TradeOpportunityState.States.Single(x => x.Code == stateCode).Id, Description = opportunity.UniqueIdentifier, LatestOpportunity = opportunity, LastestUpdate = DateTime.UtcNow }); } ); tradeOpp.LatestOpportunity = opportunity; tradeOpp.LastestUpdate = DateTime.UtcNow; // Before writing, we lock the semaphore. await tradeOpp.LockingSemaphore.WaitAsync(); // Lock try { // Otherwise the opportunity is still valid. // If this is a new opportunity, then we definitely need to save to DB. if (isNewOpportunity) { using (var ctx = new RBBotContext()) { requirements.ForEach(x => { x.TradeOpportunity = tradeOpp; tradeOpp.TradeOpportunityRequirements.Add(x); }); tradeOpp.TradeOpportunityValues.Add(newTradeValue); newTradeValue.TradeOpportunity = tradeOpp; ctx.TradeOpportunities.Add(tradeOpp); ctx.TradeOpportunityRequirements.AddRange(requirements); ctx.TradeOpportunityValues.Add(newTradeValue); await ctx.SaveChangesAsync(); } } else { // Else we take the requirements and see if anything changed. var requirementsToAdd = new List <TradeOpportunityRequirement>(); foreach (var req in requirements) { var toReq = tradeOpp.TradeOpportunityRequirements.Where(x => x.ItemIdentifier == req.ItemIdentifier && x.TradeOpportunityRequirementType == req.TradeOpportunityRequirementType).OrderBy(x => x.Timestamp).LastOrDefault(); if (toReq == null || toReq.RequirementMet != req.RequirementMet) { requirementsToAdd.Add(req); req.TradeOpportunityId = tradeOpp.Id; } } newTradeValue.TradeOpportunityId = tradeOpp.Id; using (var ctx = new RBBotContext()) { ctx.TradeOpportunityRequirements.AddRange(requirementsToAdd); ctx.TradeOpportunityValues.Add(newTradeValue); await ctx.SaveChangesAsync(); } } } finally { tradeOpp.LockingSemaphore.Release(); } // Return the trade opportunity object. return(tradeOpp); }
public static async Task EndTradeOpportunity(TradeOpportunity opportunity, TradeOpportunityState finalState, TradeOpportunityValue finalOpportunityValue = null, TradeActionResponse tradeActionResponse = null) { // Note: This was initially written in a way that I first remove the trade opp from the concurrent dictionary and then write to the DB. // It was wrong as by the time we got back a response from the db, other same opportunities were being written! This caused a lot of same opportunities to be written! // Therefore what we do here is first we singal the removal from the database and only once this is done we try to remove it from the concurrent dictionary. // For this reason the trade opportunity is immediately locked. // This is the part we persist to the db. We first wait for // any possible semaphore on the opportunity and release it as soon as we're done. await opportunity.LockingSemaphore.WaitAsync(); // It might have happened that this opportunity was already written to the db. Ignore this call. if (opportunity.IsDbExecutedWritten) { return; } try { // Persist to db using (var ctx = new RBBotContext()) { opportunity.TradeOpportunityStateId = finalState.Id; opportunity.EndTime = DateTime.UtcNow; // If a final value is specified, add it now. if (finalOpportunityValue != null) { finalOpportunityValue.TradeOpportunityId = opportunity.Id; ctx.TradeOpportunityValues.Add(finalOpportunityValue); } // With transactions we also look into the accounts and update them. if (tradeActionResponse != null) { tradeActionResponse.Transactions.ToList().ForEach(x => x.TradeOpportunity = opportunity); ctx.TradeOpportunityTransactions.AddRange(tradeActionResponse.Transactions); foreach (var acc in opportunity.LatestOpportunity.GetAffectedAccounts()) { ctx.TradeAccounts.Attach(acc); ctx.Entry(acc).State = System.Data.Entity.EntityState.Modified; } } ctx.TradeOpportunities.Attach(opportunity); ctx.Entry(opportunity).State = System.Data.Entity.EntityState.Modified; await ctx.SaveChangesAsync(); opportunity.IsDbExecutedWritten = true; // This is an unmapped property that is used to make sure that if by any chance this trade opportunity is tried to be ended again, it won't succeed! // Now that we've written to the db, try removing it from the concurrent dictionary. TradeOpportunity time = null; bool removedSuccess = tradeOpportunities.TryRemove(opportunity.LatestOpportunity.UniqueIdentifier, out time); if ((removedSuccess) && (OnOpportunityExpired != null)) // If the opportunity could be removed, then raise event!. { OnOpportunityExpired(opportunity); } } } finally { opportunity.LockingSemaphore.Release(); } }
private static async Task SynchronizeAccounts(IExchangeTrader exchangeIntegration, RBBotContext dbContext) { Exchange exchangeModel = exchangeIntegration.Exchange; // Get the balances from the exchange integration var exchangebalances = (await exchangeIntegration.GetBalancesAsync()).ToDictionary(x => x.CurrencyCode, y => y); // Get the exchange's trading accounts. var existingAccounts = exchangeModel.TradeAccounts.ToDictionary(x => x.Currency.Code, y => y); // If the account exists already, then update. existingAccounts.Where(x => exchangebalances.Keys.Contains(x.Key)).ToList().ForEach(x => { var exCurr = exchangebalances[x.Key]; var acc = existingAccounts[x.Key]; acc.Balance = exCurr.Balance; acc.LastUpdate = exCurr.Timestamp; if (exCurr.ExchangeIdentifier != null) { acc.ExchangeIdentifier = exCurr.ExchangeIdentifier; } if (exCurr.Address != null) { acc.Address = exCurr.Address; } }); // If the account doesn't exist, then create it. exchangebalances.Keys.Where(x => !existingAccounts.Keys.Contains(x)).ToList().ForEach(x => { var b = exchangebalances[x]; TradeAccount acc = new TradeAccount() { Address = b.Address, Balance = b.Balance, Exchange = exchangeModel, ExchangeIdentifier = b.ExchangeIdentifier, LastUpdate = b.Timestamp, Currency = dbContext.Currencies.Where(c => c.Code == b.CurrencyCode).Single() }; dbContext.TradeAccounts.Add(acc); }); }
public static async Task InitializeEngine(IMarketPriceObserver[] priceObservers) { IList <Exchange> exchangeModels = null; IList <Setting> settings = null; List <IExchange> integrations = null; // Get all exchangeModels. We need them to construct the integrations. using (var ctx = new RBBotContext()) { // Load the exchanges and all the stuff associated with them exchangeModels = ctx.Exchanges .Include(x => x.ExchangeState) .Include(x => x.Settings) .Include(x => x.ExchangeTradePairs.Select(y => y.ExchangeTradePairState)) .Include(x => x.ExchangeTradePairs.Select(y => y.TradePair).Select(z => z.FromCurrency)) .Include(x => x.ExchangeTradePairs.Select(y => y.TradePair).Select(z => z.ToCurrency)) .Where(x => x.ExchangeState.Code != "OFF") // Don't get offline exchanges! .ToList(); // Get / cache the settings. settings = ctx.Settings.ToList(); // Now since I'm lazy, i first put setting unencrypted in db, and then encrypt them here. if (settings.Where(x => x.IsEncrypted == false && x.ShouldBeEncrypted == true).Any(x => { x.EncryptSetting(); return(true); })) { ctx.SaveChanges(); } // Now initialize the setting helper with all settings! SettingHelper.InitializeSettings(settings.ToArray()); // Initialize all exchanges and their integrations. var ccExchangeIds = settings.Where(x => x.Name == "ReadFromCryptoCompare" && x.Value.ToLower() == "true").Select(x => x.ExchangeId).ToList(); var ccExchanges = exchangeModels.Where(x => ccExchangeIds.Contains(x.Id)).ToList(); integrations = new List <Exchanges.IExchange>(); integrations.Add(new CryptoCompareIntegration(priceObservers, ccExchanges.ToArray())); integrations.Add(new BitflyerIntegration(priceObservers, new[] { exchangeModels.Single(x => x.Name == "Bitflyer") })); integrations.Add(new GDAXIntegration(priceObservers, new[] { exchangeModels.Single(x => x.Name == "GDAX") })); //integrations.Add(new OKCoinComIntegration(priceObservers, new[] { exchangeModels.Single(x => x.Name == "OKCoin.com") })); //integrations.Add(new OKCoinCNIntegration(priceObservers, new[] { exchangeModels.Single(x => x.Name == "OKCoin.cn") })); integrations.Add(new PoloniexIntegration(exchangeModels.Single(x => x.Name == "Poloniex"))); integrations.Add(new KrakenIntegration(exchangeModels.Single(x => x.Name == "Kraken"))); // Synchronize all the trading accounts. var tradingExchanges = integrations.Where(x => x is IExchangeTrader).Select(x => x as IExchangeTrader).ToList(); foreach (var te in tradingExchanges) { await SynchronizeAccounts(te, ctx); } await ctx.SaveChangesAsync(); } var readerExchanges = integrations.Where(x => x is IExchangePriceReader).Select(x => x as IExchangePriceReader).ToList(); foreach (var e in readerExchanges) { await e.InitializeExchangePriceProcessingAsync(); } }
public static async Task <IExchange[]> InitializeEngine(bool isSimulation = true) { IList <Exchange> exchangeModels = null; IList <Setting> settings = null; List <IExchange> integrations = null; // Get all exchangeModels. We need them to construct the integrations. using (var ctx = new RBBotContext()) { // Load the exchanges and all the stuff associated with them exchangeModels = ctx.Exchanges .Include(x => x.ExchangeState) .Include(x => x.Settings) .Include(x => x.TradeAccounts) .Include(x => x.ExchangeTradePairs.Select(y => y.ExchangeTradePairState)) .Include(x => x.ExchangeTradePairs.Select(y => y.TradePair).Select(z => z.FromCurrency)) .Include(x => x.ExchangeTradePairs.Select(y => y.TradePair).Select(z => z.ToCurrency)) .Where(x => x.ExchangeState.Code != "OFF") // Don't get offline exchanges! .ToList(); // Get / cache the settings. settings = ctx.Settings.ToList(); // Now since I'm lazy, i first put setting unencrypted in db, and then encrypt them here. if (settings.Where(x => x.IsEncrypted == false && x.ShouldBeEncrypted == true).Any(x => { x.EncryptSetting(); return(true); })) { ctx.SaveChanges(); } // Now initialize the setting helper with all settings! SettingHelper.InitializeSettings(settings.ToArray()); // Initialize system settings. SystemSetting.LoadSystemSettings(ctx); // Initialize all exchanges and their integrations. var ccExchangeIds = settings.Where(x => x.Name == "ReadFromCryptoCompare" && x.Value.ToLower() == "true").Select(x => x.ExchangeId).ToList(); var ccExchanges = exchangeModels.Where(x => ccExchangeIds.Contains(x.Id)).ToList(); integrations = new List <Exchanges.IExchange>(); integrations.Add(new CryptoCompareIntegration(ccExchanges.ToArray())); integrations.Add(new BitflyerIntegration(new[] { exchangeModels.Single(x => x.Name == "Bitflyer") })); integrations.Add(new GDAXIntegration(new[] { exchangeModels.Single(x => x.Name == "GDAX") })); //integrations.Add(new OKCoinComIntegration(priceObservers, new[] { exchangeModels.Single(x => x.Name == "OKCoin.com") })); //integrations.Add(new OKCoinCNIntegration(priceObservers, new[] { exchangeModels.Single(x => x.Name == "OKCoin.cn") })); integrations.Add(new PoloniexIntegration(exchangeModels.Single(x => x.Name == "Poloniex"))); integrations.Add(new KrakenIntegration(exchangeModels.Single(x => x.Name == "Kraken"))); // Cache other lookups TradeOpportunityRequirementType.RequirementTypes = ctx.TradeOpportunityRequirementTypes.ToArray(); TradeOpportunityState.States = ctx.TradeOpportunityStates.ToArray(); TradeOpportunityType.Types = ctx.TradeOpportunityTypes.ToArray(); // Initialize the price cache; TradePriceIndex.Initialize(exchangeModels.ToArray(), ctx.TradePairs.ToArray()); // Synchronize all the trading accounts. var tradingExchanges = integrations.Where(x => x is IExchangeTrader).Select(x => x as IExchangeTrader).ToList(); foreach (var te in tradingExchanges) { te.Exchange.TradingIntegration = te; // Set the trading integration. await SynchronizeAccounts(te, ctx, isSimulation); } await ctx.SaveChangesAsync(); } var readerExchanges = integrations.Where(x => x is IExchangePriceReader).Select(x => x as IExchangePriceReader).ToList(); foreach (var e in readerExchanges) { Task.Run(async() => e.InitializeExchangePriceProcessingAsync()); } // Return list of discovered integrations. These will be used to observe the the pricing data. return(integrations.ToArray()); }
internal async Task UpdateOpportunity(decimal newMarginValue) { this.CurrentValue = newMarginValue; // the opportunity is only considered started on first non-zero value! if (newMarginValue == 0m) { return; } using (var ctx = new RBBotContext()) { if (this.OpportunityModel == null) { this.OpportunityModel = new TradeOpportunity() { CurrencyId = this.OpportunityBaseCurrency.Id, StartTime = DateTime.UtcNow, IsExecuted = false, TradeOpportunityTypeId = ctx.TradeOpportunityTypes.Single(x => x.Code == this.TypeCode).Id }; lock (this.OpportunityModel) { // Add it to the context. ctx.TradeOpportunities.Add(this.OpportunityModel); ctx.SaveChanges(); // Don't execute this async so we avoid having the children created before the parent! } } var newTradeValue = new TradeOpportunityValue() { PotentialMargin = newMarginValue, Timestamp = DateTime.UtcNow, TradeOpportunityId = this.OpportunityModel.Id }; ctx.TradeOpportunityValues.Add(newTradeValue); //this.OpportunityModel.TradeOpportunityValues.Add(newTradeValue); //ctx.Entry(this.OpportunityModel).State = System.Data.Entity.EntityState.Modified; //ctx.TradeOpportunityValues.Add(newTradeValue); try { await ctx.SaveChangesAsync(); } catch (Exception ex) { Console.WriteLine("Error saving opportunity to database: ", ex.ToString()); } // async save to db and return //await ctx.SaveChangesAsync(); // await OpportunityScoreEngine.UpdateOpportunityValue(this); } }