public async Task SendNotification(NotificationRule notificationRule, MarketInstrument instrument, CandlePayload firstCandle, CandlePayload lastCandle) { if (_notificationTimestampToTicker == null) { await LoadLastSendTimes(); } try { if (!_notificationTimestampToTicker.ContainsKey(instrument.Ticker) || (_notificationTimestampToTicker.ContainsKey(instrument.Ticker) && DateTime.UtcNow - _notificationTimestampToTicker[instrument.Ticker] >= TimeSpan.FromHours(notificationRule.TimePeriodInHours))) { var msg = MakeNotificationMessage(notificationRule, instrument, firstCandle, lastCandle); await _telegramBot.SendNotification(msg); _notificationTimestampToTicker[instrument.Ticker] = DateTime.UtcNow; await SaveLastSendTimes(); } } catch (Exception ex) { Log.Error(ex.ToString()); await Task.Delay(TimeSpan.FromSeconds(1)); } }
public override IStockModel CreateStockModel(MarketInstrument instrument) { var stock = base.CreateStockModel(instrument); //_hub.Clients.All.SendAsync("stock", stock); return(stock); }
private async Task <CandlePayload?> MeetRequirements(MarketInstrument instrument, CancellationToken ct) { try { var candles = await _contextProvider.Do(ctx => ctx.MarketCandlesAsync( instrument.Figi, _timeProvider.Today.Subtract(TimeSpan.FromDays(7)), _timeProvider.Today, CandleInterval.Day ), ct); ct.ThrowIfCancellationRequested(); var dayCandle = candles.Candles.LastOrDefault(); if (dayCandle == null) { return(null); } var body = dayCandle.Close - dayCandle.Open; if (body >= 0) { return(null); } var shadow = dayCandle.Low - dayCandle.Close; return(shadow / body < 0.2M ? dayCandle : null); } catch (Exception ex) { Console.WriteLine($"{ex}"); return(null); } }
private async Task HandleInstrument(Context context, MarketInstrument instrument, NotificationRule notificationRule) { // try get candles var timePeriodInHours = notificationRule.TimePeriodInHours; var interval = CandleInterval.FiveMinutes; if (timePeriodInHours >= 12) { interval = CandleInterval.Hour; } var start = DateTime.UtcNow.AddHours(-2.5 * timePeriodInHours); var end = DateTime.UtcNow; var candles = await context.MarketCandlesAsync(instrument.Figi, start, end, interval); if (candles.Candles.Count == 0) { Log.Debug($"No candles for {instrument.Ticker}\t{start:dd.MM.yy HH:mm} - {end:dd.MM.yy HH:mm}"); await Task.Delay(_sleepTime); return; } // handle a candle with maximum timestamp var lastCandle = candles.Candles.OrderByDescending(i => i.Time).First(); // try to find a previuos candle var previousCandleTimestamp = lastCandle.Time.AddHours(-1 * timePeriodInHours); var previousCandles = candles.Candles.Where(i => i.Time <= previousCandleTimestamp).ToList(); var attempNumber = 1; //in case it is the start of the day then try to get the last price from the first previous working day while (previousCandles.Count == 0) { start = previousCandleTimestamp.AddHours(-12 * attempNumber); end = previousCandleTimestamp.AddHours(-12 * (attempNumber - 1)); Log.Debug($"Can not find a previous candle for {instrument.Ticker} for {end:dd.MM.yy HH:mm}"); Log.Debug($"Try to fetch candles for {instrument.Ticker} for {previousCandleTimestamp.AddHours(-12 * attempNumber):dd.MM.yy HH:mm} - {previousCandleTimestamp:dd.MM.yy HH:mm}"); previousCandles = (await context.MarketCandlesAsync(instrument.Figi, start, end, interval)).Candles; attempNumber++; continue; } var firstCandle = previousCandles.OrderByDescending(i => i.Time).First(); if (notificationRule.IsActual(firstCandle.Close, lastCandle.Close)) { await _sendNotification(notificationRule, instrument, firstCandle, lastCandle); } LogTicker(instrument, notificationRule, lastCandle, firstCandle); }
public virtual IStockModel CreateStockModel(MarketInstrument instrument) { return(new StockModel { Figi = instrument.Figi, Isin = instrument.Isin, Name = instrument.Name, Lot = instrument.Lot, MinPriceIncrement = instrument.MinPriceIncrement, Ticker = instrument.Ticker, Currency = instrument.Currency.ToString() }); }
public async Task TryAdd(MarketInstrument instrument) { var models = _repository.GetAll(); if (models.Any(m => m.Isin == instrument.Isin)) { _logger.LogTrace($"Instrument '{instrument.Isin}' already present"); return; } _logger.LogTrace($"Add instrument: {instrument}"); await _repository.Add(new AssetMetadataModel( instrument.Isin, instrument.Figi, instrument.Name, instrument.Type.ToString())); }
public static Infrastructure.Common.Models.Market.CurrencyPair ToOuterModel(this MarketInstrument source) { var target = new Infrastructure.Common.Models.Market.CurrencyPair { Id = source.Figi, BaseCurrencyId = source.Ticker, QuoteCurrencyId = source.Currency.ToString(), QuantityIncrement = source.MinPriceIncrement, TickSize = source.Lot, FeeCurrencyId = source.Currency.ToString() }; return(target); }
public static YesterdayCandleData Create(MarketInstrument stock, CandlePayload candle) { return(new YesterdayCandleData( stock.Figi, stock.Name, Convert.ToInt32(candle.Volume), candle.Open, candle.Close, candle.Low, candle.High, stock.Currency, stock.MinPriceIncrement )); }
public async Task MarketSearchByFigiTest() { const string figi = Figi; _handler.Expect(HttpMethod.Get, $"{BaseUri}market/search/by-figi") .WithQueryString("figi", figi) .WithoutContent() .RespondJsonFromFile("market-search-by-figi-response"); var instrumentList = await _context.MarketSearchByFigiAsync(figi); var expected = new MarketInstrument(figi, "NFLX", "US64110L1061", 0.01m, 1, Currency.Usd, "Netflix", InstrumentType.Stock); instrumentList.Should().BeEquivalentTo(expected); }
public TestingTrading(MarketInstrument activeInstrument) { InitializeComponent(); if (activeInstrument == null) { throw new ArgumentNullException(); } TradingChart.instrument = new Instrument(activeInstrument); DataContext = this; IntervalComboBox.SelectedIndex = 5; StrategyComboBox.SelectedIndex = 0; }
private static void LogTicker(MarketInstrument instrument, NotificationRule notificationRule, CandlePayload lastCandle, CandlePayload firstCandle) { var priceChange = lastCandle.Close - firstCandle.Close; var priceChangeInPercent = 100 * priceChange / firstCandle.Close; var msg = $"${instrument.Ticker} " + $"{notificationRule.TimePeriodInHours}h / {priceChangeInPercent.ToString("F2")} % / {(priceChange >= 0 ? "+" : "")}{priceChange.ToString("F2")} {instrument.Currency} / " + $"{firstCandle.Close} {instrument.Currency} -> {lastCandle.Close} {instrument.Currency} / " + $"{firstCandle.Time:dd.MM.yyyy HH:mm} -> {lastCandle.Time:dd.MM.yyyy HH:mm}"; Log.Write(notificationRule.IsActual(firstCandle.Close, lastCandle.Close) ? Serilog.Events.LogEventLevel.Warning : Serilog.Events.LogEventLevel.Information, msg); }
public static Instrument ToInstrument(this MarketInstrument marketInstrument) { if (marketInstrument == null) { return(null); } return(new Instrument { Figi = marketInstrument.Figi, Isin = marketInstrument.Isin, Ticker = marketInstrument.Ticker, Lot = marketInstrument.Lot, MinPriceIncrement = marketInstrument.MinPriceIncrement, Name = marketInstrument.Name, Type = marketInstrument.Type, Currency = marketInstrument.Currency }); }
private Instrument MarketInstrumentConverter(MarketInstrument marketInstrument) { if (marketInstrument == null) { return(null); } return(new Instrument { Figi = marketInstrument.Figi, Isin = marketInstrument.Isin, Ticker = marketInstrument.Ticker, Lot = marketInstrument.Lot, MinPriceIncrement = marketInstrument.MinPriceIncrement, Name = marketInstrument.Name, Type = marketInstrument.Type, Currency = marketInstrument.Currency }); }
public async Task MarketSearchByTickerTest() { var handler = new HttpMessageHandlerStub(HttpStatusCode.OK, "{\"trackingId\":\"QBASTAN\",\"status\":\"OK\",\"payload\":{\"figi\":\"BBG000CL9VN6\",\"ticker\":\"NFLX\",\"isin\":\"US64110L1061\",\"minPriceIncrement\":0.01,\"lot\":1,\"currency\":\"USD\"}}"); var connection = new Connection(BaseUri, WebSocketBaseUri, Token, new HttpClient(handler)); var context = connection.Context; var instrument = await context.MarketSearchByTickerAsync("NFLX"); Assert.NotNull(handler.RequestMessage); Assert.Equal(HttpMethod.Get, handler.RequestMessage.Method); Assert.Equal(new Uri($"{BaseUri}market/search/by-ticker?ticker=NFLX"), handler.RequestMessage.RequestUri); Assert.Null(handler.RequestMessage.Content); Assert.NotNull(instrument); var expected = new MarketInstrument("BBG000CL9VN6", "NFLX", "US64110L1061", 0.01m, 1, Currency.Usd); Assert.Equal(expected.Figi, instrument.Figi); Assert.Equal(expected.Ticker, instrument.Ticker); Assert.Equal(expected.Isin, instrument.Isin); Assert.Equal(expected.MinPriceIncrement, instrument.MinPriceIncrement); Assert.Equal(expected.Lot, instrument.Lot); Assert.Equal(expected.Currency, instrument.Currency); }
public RealTimeTrading(MarketInstrument activeInstrument) { InitializeComponent(); if (activeInstrument == null) { throw new ArgumentNullException(); } TradingChart.instrument = new Instrument(activeInstrument); candlesTimer = new Timer(e => CandlesTimerElapsed(), null, TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(0.3) ); DataContext = this; ShowBalance(); IntervalComboBox.SelectedIndex = 5; StrategyComboBox.SelectedIndex = 0; }
public MarketInstrumentSearchResponse([JsonProperty("payload")] MarketInstrument instrument) { Instrument = instrument; }
private static string MakeNotificationMessage(NotificationRule notificationRule, MarketInstrument instrument, CandlePayload firstCandle, CandlePayload lastCandle) { var priceChange = lastCandle.Close - firstCandle.Close; var priceChangeInPercent = 100 * priceChange / firstCandle.Close; var msg = $"{(notificationRule.PriceDirection == PriceDirection.Decrased ? "📉" : "📈") } " + $"${instrument.Ticker} {lastCandle.Close} {instrument.Currency}\r\n" + $"{notificationRule.TimePeriodInHours}h / {priceChangeInPercent.ToString("F2")} % / {(priceChange >= 0 ? "+" : "")}{priceChange.ToString("F2")} {instrument.Currency}\r\n" + $"{"https://www.tinkoff.ru/invest/stocks/"}{instrument.Ticker}"; return(msg); }
public TinknoffInvestTicker(Exchange e, MarketInstrument instrument) : base(e) { Instrument = instrument; }