/// <summary>Handle SnR levels added/removed</summary> private void HandleSnRLevelsChanging(object sender, ListChgEventArgs <SnRLevel> e) { switch (e.ChangeType) { case ListChg.ItemAdded: { // Watch for changes to the SnRLevel so we can update the DB e.Item.PropertyChanged += HandleSnRLevelChanged; HandleSnRLevelChanged(e.Item); break; } case ListChg.ItemRemoved: { // Stop watching for changes e.Item.PropertyChanged -= HandleSnRLevelChanged; // Remove this level from the db. // Note: this isn't called during shutdown because this event handler // is removed from the binding source before it is disposed. m_db.Execute(SqlExpr.RemoveSnRLevel(), 1, SqlExpr.RemoveSnRLevelParams(e.Item)); break; } } }
private void OnPriceDataUpdated() { PriceDataUpdated.Raise(this); // Write the price data into the db if (m_suspend_db_updates == 0 && !Equals(PriceData, PriceData.Default)) { m_db.Execute(SqlExpr.UpdatePriceData(), 1, SqlExpr.UpdatePriceDataParams(PriceData)); } }
/// <summary>Handle an individual SnR level being updated</summary> private void HandleSnRLevelChanged(object sender, PropertyChangedEventArgs e = null) { if (m_suspend_db_updates != 0) { return; } // Update this SnR level in the db var snr = (SnRLevel)sender; m_db.Execute(SqlExpr.UpdateSnRLevel(), 1, SqlExpr.UpdateSnRLevelParams(snr)); }
public Instrument(MainModel model, string symbol) { m_cache = new List <Candle>(CacheSize); Model = model; SymbolCode = symbol; SupportResistLevels = new BindingSource <SnRLevel> { DataSource = new BindingListEx <SnRLevel>() }; // Load the sqlite database of historic price data m_db = new Sqlite.Database(DBFilepath); // Tweak some DB settings for performance m_db.Execute(Sqlite.Sql("PRAGMA synchronous = OFF")); m_db.Execute(Sqlite.Sql("PRAGMA journal_mode = MEMORY")); // Create a table for the price data info m_db.Execute(SqlExpr.PriceDataTable()); // Ensure tables exist for each of the time frames. // Note: timestamp is not the row id. Row Ids should not have any // meaning. It's more efficient to let the DB do the sorting // and just use 'order by' in queries foreach (var tf in Enum <ETimeFrame> .Values.Where(x => x != ETimeFrame.None)) { m_db.Execute(SqlExpr.CandleTable(tf)); } // Create a table for the SnR levels m_db.Execute(SqlExpr.SnRLevelsTable()); // Initialise from the DB, so don't write back to the db using (Scope.Create(() => ++ m_suspend_db_updates, () => -- m_suspend_db_updates)) { // Select the default time frame to begin with TimeFrame = Settings.General.DefaultTimeFrame; // Load the last known price data for the instrument PriceData = m_db.EnumRows <PriceData>(SqlExpr.GetPriceData()).FirstOrDefault() ?? new PriceData(); // Load the known support and resistance levels SupportResistLevels.AddRange(m_db.EnumRows <SnRLevel>(SqlExpr.GetSnRLevelData())); // Set up the EMAs ResetEma(); } }
/// <summary>Add a batch of candles</summary> public void Add(ETimeFrame tf, PriceCandles candles) { // Insert the candles into the database using (var t = m_db.NewTransaction()) using (var query = new Sqlite.Query(m_db, SqlExpr.InsertCandle(tf))) { foreach (var candle in candles.AllCandles.Select(x => new Candle(x))) { query.Reset(); query.BindParms(1, SqlExpr.InsertCandleParams(candle)); query.Run(); } t.Commit(); } // Don't bother maintaining the cache, just invalidate it InvalidateCachedData(); // Record the last time data was received LastUpdatedUTC = Model.UtcNow; // Notify data added/changed DataChanged.Raise(this, new DataEventArgs(this, tf, null, false)); }
/// <summary>Add a candle value to the data</summary> public void Add(ETimeFrame tf, PriceCandle price_candle) { // Sanity check var candle = new Candle(price_candle); Debug.Assert(candle.Valid()); Debug.Assert(candle.Timestamp != 0); Debug.Assert(tf != ETimeFrame.None); // If this candle is the newest we've seen, then a new candle has started. // Get the latest candle before inserting 'candle' into the database var new_candle = tf == TimeFrame && candle.Timestamp > Latest.Timestamp; // Insert the candle into the database if the sim isn't running. // The sim draws it's data from the database, so there's no point in writing // the same data back in. Plus it might in the future do things to emulate sub-candle // updates, which I don't want to overwrite the actual data. if (!Model.SimActive) { m_db.Execute(SqlExpr.InsertCandle(tf), 1, SqlExpr.InsertCandleParams(candle)); } // If the candle is for the current time frame and within the // cached data range, update the cache to avoid invalidating it. if (tf == TimeFrame) { // Within the currently cached data? // Note: this is false if 'candle' is the start of a new candle if (!new_candle) { // Find the index in the cache for 'candle'. If not an existing cache item, then just reset the cache var cache_idx = m_cache.BinarySearch(x => x.Timestamp.CompareTo(candle.Timestamp)); if (cache_idx >= 0) { m_cache[cache_idx].Update(candle); } else { InvalidateCachedData(); } } // If the cached range ends at the latest candle (excluding the new candle) // then we can preserve the cache and append the new candle to the cache data else if (m_index_range.Endi == Count) { // If adding 'candle' will make the cache too big, just flush if (m_cache.Count > MaxCacheSize) { InvalidateCachedData(); } // Otherwise, append the new candle to the cache else { m_cache.Add(candle); m_index_range.End++; m_impl_count = null; m_impl_total = null; m_latest = null; } } // Otherwise the candle is not within the cache, just invalidate else { InvalidateCachedData(); } } // Record the last time data was received LastUpdatedUTC = Model.UtcNow; // Notify data added/changed OnDataChanged(new DataEventArgs(this, tf, candle, new_candle)); }