/// <summary>Called when a new user account has been assigned</summary> private void HandleAcctChanged(object sender = null, EventArgs e = null) { // Ensure closed Util.Dispose(ref m_db); // If there is no account id, don't open a database if (Model == null || !Model.Acct.AccountId.HasValue()) { return; } // Open a database of the trade history and other user account related info var filepath = Path_.CombinePath(Settings.General.AcctDataCacheDir, "Acct_{0}.db".Fmt(Model.Acct.AccountId)); m_db = new Sqlite.Database(filepath); // Tweak some DB settings for performance m_db.Execute(Sqlite.Sql("PRAGMA synchronous = OFF")); m_db.Execute(Sqlite.Sql("PRAGMA journal_mode = MEMORY")); // Ensure the table of trades exists var sql = Sqlite.Sql("create table if not exists ", Table.TradeHistory, " (\n", "[", nameof(Trade.Id), "] text primary key,\n", "[", nameof(Trade.SymbolCode), "] text)"); m_db.Execute(sql); // Ensure the table of orders exists sql = Sqlite.Sql("create table if not exists ", Table.Orders, " (\n", "[", nameof(Order.Id), "] integer primary key,\n", "[", ("TradeId"), "] text,\n", "[", nameof(Order.SymbolCode), "] text,\n", "[", nameof(Order.TradeType), "] integer)"); m_db.Execute(sql); }
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 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)); }