程序员的量化交易之路(35)--Lean之DataFeed数据槽3

MT4行情datafeed接口二次开发简介 插件为dll格式,需要导出以下三个方法DsCreate(); DsDestroy(); DsVersion();主要是要在 DsCreate 方法返回一个MT4要求的CFeedInterface类的子类。插件内部通过实现该类来提供相关数据源。CFeedInterface声明如下class CFeedInterface { public: //---- virtual methods 阅读详情

转载需注明出处:http://blog.youkuaiyun.com/minimicall,http://cloudtrade.top/


Lean引擎的模块划分非常的规范。其中DataFeed是数据槽,就是供应数据的模块。

1. IDataFeed 接口

模块的接口为:

namespace QuantConnect.Lean.Engine.DataFeeds
{
    /// <summary>
    /// Datafeed interface for creating custom datafeed sources.
    /// 数据供应的借口
    /// </summary>
    public interface IDataFeed
    {
        /******************************************************** 
        * INTERFACE PROPERTIES
        *********************************************************/
        /// <summary>
        /// List of the subscription the algorithm has requested. Subscriptions contain the type, sourcing information and manage the enumeration of data.
        /// 订阅列表
        /// </summary>
        List<SubscriptionDataConfig> Subscriptions
        {
            get;
        }


        /// <summary>
        /// Prices of the datafeed this instant for dynamically updating security values (and calculation of the total portfolio value in realtime).
        /// 实时价格
        /// </summary>
        /// <remarks>Indexed in order of the subscriptions</remarks>
        List<decimal> RealtimePrices
        {
            get;
        }

        /// <summary>
        /// Cross-threading queues so the datafeed pushes data into the queue and the primary algorithm thread reads it out.
        /// 跨线程使用的队列,datafeed线程放入数据,算法主线程读出数据
        /// </summary>
        ConcurrentQueue<List<BaseData>>[] Bridge
        {
            get;
            set;
        }

        /// <summary>
        /// Boolean flag indicating there is no more data in any of our subscriptions.
        /// </summary>
        bool EndOfBridges
        {
            get;
        }

        /// <summary>
        /// Array of boolean flags indicating the data status for each queue/subscription we're tracking.
        /// </summary>
        bool[] EndOfBridge
        {
            get;
        }

        /// <summary>
        /// Set the source of the data we're requesting for the type-readers to know where to get data from.
        /// </summary>
        /// <remarks>Live or Backtesting Datafeed</remarks>
        DataFeedEndpoint DataFeed
        {
            get;
            set;
        }

        /// <summary>
        /// Public flag indicator that the thread is still busy.
        /// 设置该线程是否活跃
        /// </summary>
        bool IsActive
        {
            get;
        }

        /// <summary>
        /// The most advanced moment in time for which the data feed has completed loading data
        /// </summary>
        DateTime LoadedDataFrontier { get; }

        /// <summary>
        /// Data has completely loaded and we don't expect any more.
        /// </summary>
        bool LoadingComplete
        {
            get;
        }

        /******************************************************** 
        * INTERFACE METHODS
        *********************************************************/
        /// <summary>
        /// Primary entry point.
        /// </summary>
        void Run();


        /// <summary>
        /// External controller calls to signal a terminate of the thread.
        /// </summary>
        void Exit();


        /// <summary>
        /// Purge all remaining data in the thread.
        /// </summary>
        void PurgeData();
    }
}
IDataFeed是数据槽接口,是其他实现类必须实现的。

2. BaseDataFeed 数据槽基类

它实现IDataFeed,并且是其他派生类的一个基类。

namespace QuantConnect.Lean.Engine.DataFeeds
{
    /// <summary>
    /// Common components of a data feed allowing the extender to implement only the parts which matter.
    /// 数据槽的基类,允许派生类定制部分
    /// </summary>
    public abstract class BaseDataFeed : IDataFeed
    {
        /******************************************************** 
        * CLASS VARIABLES
        *********************************************************/
        private IAlgorithm _algorithm;
        private BacktestNodePacket _job;
        private bool _endOfStreams = false;
        private int _subscriptions = 0;
        private int _bridgeMax = 500000;
        private bool _exitTriggered = false;

        private DateTime[] _frontierTime;

        /******************************************************** 
        * CLASS PROPERTIES
        *********************************************************/
        /// <summary>
        /// List of the subscription the algorithm has requested. Subscriptions contain the type, sourcing information and manage the enumeration of data.
        /// 订阅列表信息
        /// </summary>
        public List<SubscriptionDataConfig> Subscriptions { get; private set; }

        /// <summary>
        /// Prices of the datafeed this instant for dynamically updating security values (and calculation of the total portfolio value in realtime).
        /// 实时价格
        /// </summary>
        /// <remarks>Indexed in order of the subscriptions</remarks>
        public List<decimal> RealtimePrices { get; private set; }

        /// <summary>
        /// Cross-threading queues so the datafeed pushes data into the queue and the primary algorithm thread reads it out.
        /// 桥
        /// </summary>
        public ConcurrentQueue<List<BaseData>>[] Bridge { get; set; }

        /// <summary>
        /// Stream created from the configuration settings.
        /// 配置产生的流
        /// </summary>
        public SubscriptionDataReader[] SubscriptionReaderManagers { get; set; }

        /// <summary>
        /// Set the source of the data we're requesting for the type-readers to know where to get data from.
        /// </summary>
        /// <remarks>Live or Backtesting Datafeed</remarks>
        public DataFeedEndpoint DataFeed { get; set; }

        /// <summary>
        /// Flag indicating the hander thread is completely finished and ready to dispose.
        /// </summary>
        public bool IsActive { get; private set; }

        /// <summary>
        /// Flag indicating the file system has loaded all files.
        /// </summary>
        public bool LoadingComplete { get; private set; }

        /// <summary>
        /// Furthest point in time that the data has loaded into the bridges.
        /// </summary>
        public DateTime LoadedDataFrontier { get; private set; }

        /// <summary>
        /// Signifying no more data across all bridges
        /// </summary>
        public bool EndOfBridges
        {
            get
            {
                for (var i = 0; i < Bridge.Length; i++)
                {
                    if (Bridge[i].Count != 0 || EndOfBridge[i] != true)
                    {
                        return false;
                    }
                }
                return true;
            }
        }

        /// <summary>
        /// End of Stream for Each Bridge:
        /// </summary>
        public bool[] EndOfBridge { get; set; }

        /******************************************************** 
        * CLASS CONSTRUCTOR
        *********************************************************/
        /// <summary>
        /// Create an instance of the base datafeed.
        /// </summary>
        public BaseDataFeed(IAlgorithm algorithm, BacktestNodePacket job)
        {
            //Save the data subscriptions
            Subscriptions = algorithm.SubscriptionManager.Subscriptions;//是一个链表,每个节点代表了对一种证券资产数据的订阅
            _subscriptions = Subscriptions.Count;//订阅了证券数目

            //Public Properties:
            DataFeed = DataFeedEndpoint.FileSystem;//默认赋予从文件系统读取
            IsActive = true;//线程是否活跃
            Bridge = new ConcurrentQueue<List<BaseData>>[_subscriptions];//桥是一个链表的链表
            EndOfBridge = new bool[_subscriptions];
            SubscriptionReaderManagers = new SubscriptionDataReader[_subscriptions];//初始化读者列表
            RealtimePrices = new List<decimal>(_subscriptions);//初始化实时价格数据列表 
            _frontierTime = new DateTime[_subscriptions];

            //Class Privates:
            _job = job;//相关任务
            _algorithm = algorithm;//相关算法
            _endOfStreams = false;
            _bridgeMax = _bridgeMax / _subscriptions;

            //Initialize arrays:
            for (var i = 0; i < _subscriptions; i++)
            {
                _frontierTime[i] = job.PeriodStart;
                EndOfBridge[i] = false;
                Bridge[i] = new ConcurrentQueue<List<BaseData>>();//分配每个订阅桥节点的数据链表
                //为每个订阅分配读者
                SubscriptionReaderManagers[i] = new SubscriptionDataReader(Subscriptions[i], algorithm.Securities[Subscriptions[i].Symbol], DataFeedEndpoint.Database, job.PeriodStart, job.PeriodFinish);
            
            }
        }


        /// <summary>
        /// Launch the primary data thread.
        /// 读数据的线程主函数
        /// </summary>
        public virtual void Run()
        {
            while (!_exitTriggered && IsActive && !EndOfBridges)
            {
                for (var i = 0; i < Subscriptions.Count; i++)
                {
                    //With each subscription; fetch the next increment of data from the queues:
                    //为每一个订阅,读取下一个数据
                    var subscription = Subscriptions[i];//第i个证券订阅

                    if (Bridge[i].Count < 10000 && !EndOfBridge[i])//确定该证券读取的数据个数没有超出界限
                    {
                        var data = GetData(subscription);//读取数据的函数,返回数据

                        //Comment out for live databases, where we should continue asking even if no data.
                        if (data.Count == 0)//如果这个订阅没有数据,那么这个订阅就读取结束,跳到下一个订阅读取
                        {
                            EndOfBridge[i] = true;//本订阅读取结束
                            continue;
                        }

                        ////Insert data into bridge, each list is time-grouped. Assume all different time-groups.
                        foreach (var obj in data)
                        {
                            Bridge[i].Enqueue(new List<BaseData>() { obj });
                        }
                        
                        ////Record the furthest moment in time.
                        _frontierTime[i] = data.Max(bar => bar.Time);
                    }
                }
                //Set the most backward moment in time we've loaded
                LoadedDataFrontier = _frontierTime.Min();
            }

            IsActive = false;
        }


        /// <summary>
        /// Get the next set of data for this subscription
        /// 获取该订阅的下一集合数据
        /// </summary>
        /// <param name="subscription"></param>
        /// <returns></returns>
        public abstract  List<BaseData> GetData(SubscriptionDataConfig subscription);


        /// <summary>
        /// Send an exit signal to the thread.
        /// </summary>
        public virtual void Exit()
        {
            _exitTriggered = true;
            PurgeData();
        }

        /// <summary>
        /// Loop over all the queues and clear them to fast-quit this thread and return to main.
        /// </summary>
        public virtual void PurgeData()
        {
            foreach (var t in Bridge)
            {
                t.Clear();
            }
        }
    }
}
3  FileSystemDataFeed文件系统数据槽

namespace QuantConnect.Lean.Engine.DataFeeds
{
    /******************************************************** 
    * CLASS DEFINITIONS
    *********************************************************/
    /// <summary>
    /// Historical datafeed stream reader for processing files on a local disk.
    /// 从本地磁盘加载历史数据
    /// </summary>
    /// <remarks>Filesystem datafeeds are incredibly fast</remarks>
    public class FileSystemDataFeed : IDataFeed
    {
        /******************************************************** 
        * CLASS VARIABLES
        *********************************************************/
        // Set types in public area to speed up:
        private IAlgorithm _algorithm;
        private BacktestNodePacket _job;
        private bool _endOfStreams = false;
        private int _subscriptions = 0;
        private int _bridgeMax = 500000;
        private bool _exitTriggered = false;

        /******************************************************** 
        * CLASS PROPERTIES
        *********************************************************/
        /// <summary>
        /// List of the subscription the algorithm has requested. Subscriptions contain the type, sourcing information and manage the enumeration of data.
        /// </summary>
        public List<SubscriptionDataConfig> Subscriptions { get; private set; }

        /// <summary>
        /// Prices of the datafeed this instant for dynamically updating security values (and calculation of the total portfolio value in realtime).
        /// </summary>
        /// <remarks>Indexed in order of the subscriptions</remarks>
        public List<decimal> RealtimePrices { get; private set; } 

        /// <summary>
        /// Cross-threading queues so the datafeed pushes data into the queue and the primary algorithm thread reads it out.
        /// </summary>
        public ConcurrentQueue<List<BaseData>>[] Bridge { get; set; }

        /// <summary>
        /// Set the source of the data we're requesting for the type-readers to know where to get data from.
        /// </summary>
        /// <remarks>Live or Backtesting Datafeed</remarks>
        public DataFeedEndpoint DataFeed { get; set; }

        /// <summary>
        /// Flag indicating the hander thread is completely finished and ready to dispose.
        /// </summary>
        public bool IsActive { get; private set; }

        /// <summary>
        /// Flag indicating the file system has loaded all files.
        /// </summary>
        public bool LoadingComplete { get; private set; }

        /// <summary>
        /// Furthest point in time that the data has loaded into the bridges.
        /// </summary>
        public DateTime LoadedDataFrontier { get; private set; }

        /// <summary>
        /// Stream created from the configuration settings.
        /// </summary>
        private SubscriptionDataReader[] SubscriptionReaders { get; set; }

        /// <summary>
        /// Signifying no more data across all bridges
        /// </summary>
        public bool EndOfBridges 
        {
            get 
            {
                for (var i = 0; i < Bridge.Length; i++)
                {
                    if (Bridge[i].Count != 0 || EndOfBridge[i] != true || _endOfStreams != true)
                    {
                        return false;
                    }
                }
                return true;
            }
        }

        /// <summary>
        /// End of Stream for Each Bridge:
        /// </summary>
        public bool[] EndOfBridge { get; set; }

        /// <summary>
        /// Frontiers for each fill forward high water mark
        /// </summary>
        public DateTime[] FillForwardFrontiers;

        /******************************************************** 
        * CLASS CONSTRUCTOR
        *********************************************************/
        /// <summary>
        /// Create a new backtesting data feed.
        /// </summary>
        /// <param name="algorithm">Instance of the algorithm</param>
        /// <param name="job">Algorithm work task</param>
        public FileSystemDataFeed(IAlgorithm algorithm, BacktestNodePacket job)
        {
            Console.WriteLine("FileSystemDataFeed,algorithm:" + algorithm + ",job: " + job);
            Subscriptions = algorithm.SubscriptionManager.Subscriptions;
            Console.WriteLine("Subscriptions.count:" + Subscriptions.Count);
            _subscriptions = Subscriptions.Count;
          

            //Public Properties:
            DataFeed = DataFeedEndpoint.FileSystem;
            IsActive = true;
            Bridge = new ConcurrentQueue<List<BaseData>>[_subscriptions];
            EndOfBridge = new bool[_subscriptions];
            SubscriptionReaders = new SubscriptionDataReader[_subscriptions];
            FillForwardFrontiers = new DateTime[_subscriptions];
            RealtimePrices = new List<decimal>(_subscriptions);

            //Class Privates:
            _job = job;
            _algorithm = algorithm;
            _endOfStreams = false;
            _bridgeMax = _bridgeMax / _subscriptions; //Set the bridge maximum count:

            for (var i = 0; i < _subscriptions; i++)
            {
                //Create a new instance in the dictionary:
                Bridge[i] = new ConcurrentQueue<List<BaseData>>();
                EndOfBridge[i] = false;

                SubscriptionReaders[i] = new SubscriptionDataReader(Subscriptions[i], _algorithm.Securities[Subscriptions[i].Symbol], DataFeed, _job.PeriodStart, _job.PeriodFinish);
                FillForwardFrontiers[i] = new DateTime();
            }
        }

        /******************************************************** 
        * CLASS METHODS
        *********************************************************/
        /// <summary>
        /// Main routine for datafeed analysis.
        /// </summary>
        /// <remarks>This is a hot-thread and should be kept extremely lean. Modify with caution.</remarks>
        public void Run()
        {
            Log.Trace("debug FileSystemDataFeed.run()");
            Console.WriteLine("FileSystemDataFeed.run()");
            //Calculate the increment based on the subscriptions:
            var tradeBarIncrements = CalculateIncrement(includeTick: false);
            var increment = CalculateIncrement(includeTick: true);

            //Loop over each date in the job
            foreach (var date in Time.EachTradeableDay(_algorithm.Securities, _job.PeriodStart, _job.PeriodFinish))
            {
                Log.Trace("in trading date:"+date+",PeriodStart:"+_job.PeriodStart+",PeriodFinish:"+_job.PeriodFinish);
                //Update the source-URL from the BaseData, reset the frontier to today. Update the source URL once per day.
                // this is really the next frontier in the future
                var frontier = date.Add(increment);
                var activeStreams = _subscriptions;
                Log.Trace("subscription:" + _subscriptions);
                //Initialize the feeds to this date:
                for (var i = 0; i < _subscriptions; i++) 
                {
                    //Don't refresh source when we know the market is closed for this security:
                    Log.Trace("i:"+i+"subscription");
                    var success = SubscriptionReaders[i].RefreshSource(date);

                    //If we know the market is closed for security then can declare bridge closed.
                    if (success) {
                        EndOfBridge[i] = false;
                    }
                    else
                    {
                        ProcessMissingFileFillForward(SubscriptionReaders[i], i, tradeBarIncrements, date);
                        EndOfBridge[i] = true;
                    }
                }

                //Pause the DataFeed
                var bridgeFullCount = Bridge.Count(bridge => bridge.Count >= _bridgeMax);
                var bridgeZeroCount = Bridge.Count(bridge => bridge.Count == 0);
                var active = GetActiveStreams();

                //Pause here while bridges are full, but allow missing files to pass
                while (bridgeFullCount > 0 && ((_subscriptions - active) == bridgeZeroCount) && !_exitTriggered)
                {
                    bridgeFullCount = Bridge.Count(bridge => bridge.Count >= _bridgeMax);
                    bridgeZeroCount = Bridge.Count(bridge => bridge.Count == 0);
                    Thread.Sleep(5);
                }

                // for each smallest resolution
                var datePlusOneDay = date.Date.AddDays(1);
                while ((frontier.Date == date.Date || frontier.Date == datePlusOneDay) && !_exitTriggered)
                {
                    var cache = new List<BaseData>[_subscriptions];
                    
                    //Reset Loop:
                    long earlyBirdTicks = 0;

                    //Go over all the subscriptions, one by one add a minute of data to the bridge.
                    //对所订阅的证券进行一个个的加载,加载到数据桥中
                    for (var i = 0; i < _subscriptions; i++)
                    {
                        //Get the reader manager:获得第i个证券的读者
                        var manager = SubscriptionReaders[i];

                        //End of the manager stream set flag to end bridge: also if the EOB flag set, from the refresh source method above
                        if (manager.EndOfStream || EndOfBridge[i])
                        {
                            EndOfBridge[i] = true;
                            activeStreams = GetActiveStreams();
                            if (activeStreams == 0)
                            {
                                frontier = frontier.Date + TimeSpan.FromDays(1);
                            }
                            continue;
                        }

                        //Initialize data store:
                        cache[i] = new List<BaseData>(2);

                        //Add the last iteration to the new list: only if it falls into this time category
                        //下面这个代码很关键,它把当前读到的数据条放到该证券对应的链表里面
                        var cacheAtIndex = cache[i];
                        while (manager.Current.EndTime < frontier)
                        {
                            Log.Trace("Current:symbol:" + manager.Current.Symbol + ",price" + manager.Current.Price);
                            cacheAtIndex.Add(manager.Current);//放Current到该证券对应的链表里面
                            Log.Trace(string.Format("FileSystemDataFeed,Current: {0}", manager.Current));
                            if (!manager.MoveNext()) break;//读取下一个数据
                        }

                        //Save the next earliest time from the bridges: only if we're not filling forward.
                        if (manager.Current != null)
                        {
                            if (earlyBirdTicks == 0 || manager.Current.EndTime.Ticks < earlyBirdTicks)
                            {
                                earlyBirdTicks = manager.Current.EndTime.Ticks;
                            }
                        }
                    }

                    if (activeStreams == 0)
                    {
                        break;
                    }

                    //Add all the lists to the bridge, release the bridge
                    //we push all the data up to this frontier into the bridge at once
                    for (var i = 0; i < _subscriptions; i++)
                    {
                        if (cache[i] != null && cache[i].Count > 0)
                        {
                            FillForwardFrontiers[i] = cache[i][0].EndTime;
                            Bridge[i].Enqueue(cache[i]);
                        }
                        ProcessFillForward(SubscriptionReaders[i], i, tradeBarIncrements);
                    }

                    //This will let consumers know we have loaded data up to this date
                    //So that the data stream doesn't pull off data from the same time period in different events
                    LoadedDataFrontier = frontier;

                    if (earlyBirdTicks > 0 && earlyBirdTicks > frontier.Ticks) 
                    {
                        //Jump increment to the nearest second, in the future: Round down, add increment
                        frontier = (new DateTime(earlyBirdTicks)).RoundDown(increment) + increment;
                    }
                    else
                    {
                        //Otherwise step one forward.
                        frontier += increment;
                    }

                } // End of This Day.

                if (_exitTriggered) break;

            } // End of All Days:

            Log.Trace(DataFeed + ".Run(): Data Feed Completed.");
            LoadingComplete = true;

            //Make sure all bridges empty before declaring "end of bridge":
            while (!EndOfBridges && !_exitTriggered)
            {
                for (var i = 0; i < _subscriptions; i++)
                {
                    //Nothing left in the bridge, mark it as finished
                    if (Bridge[i].Count == 0)
                    {
                        EndOfBridge[i] = true;
                    }
                }
                if (GetActiveStreams() == 0) _endOfStreams = true;
                Thread.Sleep(100);
            }

            //Close up all streams:
            for (var i = 0; i < Subscriptions.Count; i++)
            {
                SubscriptionReaders[i].Dispose();
            }

            Log.Trace(DataFeed + ".Run(): Ending Thread... ");
            IsActive = false;
        }



        /// <summary>
        /// Send an exit signal to the thread.
        /// 退出该线程
        /// </summary>
        public void Exit()
        {
            _exitTriggered = true;
            PurgeData();
        }


        /// <summary>
        /// Loop over all the queues and clear them to fast-quit this thread and return to main.
        /// 清除缓存
        /// </summary>
        public void PurgeData()
        {
            foreach (var t in Bridge)
            {
                t.Clear();
            }
        }

        private void ProcessMissingFileFillForward(SubscriptionDataReader manager, int i, TimeSpan increment, DateTime dateToFill)
        {
            // we'll copy the current into the next day
            var subscription = Subscriptions[i];
            if (!subscription.FillDataForward || manager.Current == null) return;

            var start = dateToFill.Date + manager.Exchange.MarketOpen;
            if (subscription.ExtendedMarketHours)
            {
                start = dateToFill.Date + manager.Exchange.ExtendedMarketOpen;
            }

            // shift the 'start' time to the end of the bar by adding the increment, this makes 'date'
            // the end time which also allows the market open functions to behave as expected

            var current = manager.Current;
            for (var endTime = start.Add(increment); endTime.Date == dateToFill.Date; endTime = endTime + increment)
            {
                if (manager.IsMarketOpen(endTime) || (subscription.ExtendedMarketHours && manager.IsExtendedMarketOpen(endTime)))
                {
                    EnqueueFillForwardData(i, current, endTime);
                }
                else
                {
                    // stop fill forwarding when we're no longer open
                    break;
                }
            }
        }

        /// <summary>
        /// If this is a fillforward subscription, look at the previous time, and current time, and add new 
        /// objects to queue until current time to fill up the gaps.
        /// </summary>
        /// <param name="manager">Subscription to process</param>
        /// <param name="i">Subscription position in the bridge ( which queue are we pushing data to )</param>
        /// <param name="increment">Timespan increment to jump the fillforward results</param>
        private void ProcessFillForward(SubscriptionDataReader manager, int i, TimeSpan increment)
        {
            // If previous == null cannot fill forward nothing there to move forward (e.g. cases where file not found on first file).
            if (!Subscriptions[i].FillDataForward || manager.Previous == null || manager.Current == null) return;

            //Last tradebar and the current one we're about to add to queue:
            var previous = manager.Previous;
            var current = manager.Current;

            // final two points of file that ends at midnight, causes issues in the day rollover/fill forward
            if (current.EndTime.TimeOfDay.Ticks == 0 && previous.EndTime == current.Time)
            {
                return;
            }

            //Initialize the frontier:
            if (FillForwardFrontiers[i].Ticks == 0) FillForwardFrontiers[i] = previous.EndTime;

            // using the previous to fill forward since 'current' is ahead the frontier
            var whatToFill = previous;
            // using current.EndTime as fill until because it's the next piece of data we have for this subscription
            var fillUntil = current.EndTime;

            //Data ended before the market closed: premature ending flag - continue filling forward until market close.
            if (manager.EndOfStream && manager.IsMarketOpen(current.EndTime))
            {
                //Make sure we only fill forward to end of *today* -- don't fill forward tomorrow just because its also open
                fillUntil = FillForwardFrontiers[i].Date.AddDays(1);
                // since we ran out of data, use the current as the clone source, it's more recent than previous
                whatToFill = current;
            }

            // loop from our last time (previous.EndTime) to our current.EndTime, filling in all missing day during
            // request market hours
            for (var endTime = FillForwardFrontiers[i] + increment; (endTime < fillUntil); endTime = endTime + increment)
            {
                if (Subscriptions[i].ExtendedMarketHours)
                {
                    if (!manager.IsExtendedMarketOpen(endTime.Subtract(increment)))
                    {
                        //If we've asked for extended hours, and the security is no longer inside extended market hours, skip:
                        continue;
                    }
                }
                else
                {
                    // if the market isn't open skip to the current.EndTime and rewind until the market is open
                    // this is the case where the previous value is from yesterday but we're trying to fill forward
                    // the next day, so instead of zooming through 18 hours of off-market hours, skip to our current data
                    // point and rewind the market open.
                    //
                    // E.g, Current.EndTime = 9:40am and Previous.EndTime = 2:00pm, so fill in from 2->4pm
                    // and then skip to 9:40am, reverse to 9:30am and fill from 9:30->9:40
                    if (!manager.IsMarketOpen(endTime.Subtract(increment)) && Subscriptions[i].Resolution != Resolution.Daily)
                    {
                        // Move fill forward so we don't waste time in this tight loop.
                        endTime = fillUntil;
                        do
                        {
                            endTime = endTime - increment;
                        }
                        // is market open assumes start time of bars, open at 9:30 closed at 4:00
                        // so decrement our date to use the start time
                        while (manager.IsMarketOpen(endTime.Subtract(increment)));
                        continue;
                    }
                }

                // add any overlap condition here
                if (Subscriptions[i].Resolution == Resolution.Daily)
                {
                    // handle fill forward on lower resolutions
                    var barStartTime = endTime - increment;
                    if (manager.Exchange.IsOpenDuringBar(barStartTime, endTime, Subscriptions[i].ExtendedMarketHours))
                    {
                        EnqueueFillForwardData(i, previous, endTime);
                    }
                    // special case catch missing days
                    else if (endTime.TimeOfDay.Ticks == 0 && manager.Exchange.DateIsOpen(endTime.Date.AddDays(-1)))
                    {
                        EnqueueFillForwardData(i, previous, endTime);
                    }
                    continue;
                }

                EnqueueFillForwardData(i, whatToFill, endTime);
            }
        }


        private void EnqueueFillForwardData(int i, BaseData previous, DateTime dataEndTime)
        {
            var cache = new List<BaseData>(1);
            var fillforward = previous.Clone(true);
            fillforward.Time = dataEndTime.Subtract(Subscriptions[i].Increment);
            fillforward.EndTime = dataEndTime;
            FillForwardFrontiers[i] = dataEndTime;
            cache.Add(fillforward);
            Bridge[i].Enqueue(cache);
        }


        /// <summary>
        /// Get the number of active streams still EndOfBridge array.
        /// </summary>
        /// <returns>Count of the number of streams with data</returns>
        private int GetActiveStreams()
        {
            //Get the number of active streams:
            var activeStreams = (from stream in EndOfBridge
                                 where stream == false
                                 select stream).Count();
            return activeStreams;
        }


        /// <summary>
        /// Calculate the minimum increment to scan for data based on the data requested.
        /// </summary>
        /// <param name="includeTick">When true the subscriptions include a tick data source, meaning there is almost no increment.</param>
        /// <returns>Timespan to jump the data source so it efficiently orders the results</returns>
        private TimeSpan CalculateIncrement(bool includeTick)
        {
            var increment = TimeSpan.FromDays(1);
            foreach (var config in Subscriptions)
            {
                switch (config.Resolution)
                {
                    //Hourly TradeBars:
                    case Resolution.Hour:
                        if (increment > TimeSpan.FromHours(1))
                        {
                            increment = TimeSpan.FromHours(1);
                        }
                        break;

                    //Minutely TradeBars:
                    case Resolution.Minute:
                        if (increment > TimeSpan.FromMinutes(1))
                        {
                            increment = TimeSpan.FromMinutes(1);
                        }
                        break;

                    //Secondly Bars:
                    case Resolution.Second:
                        if (increment > TimeSpan.FromSeconds(1))
                        {
                            increment = TimeSpan.FromSeconds(1);
                        }
                        break;

                    //Ticks: No increment; just fire each data piece in as they happen.
                    case Resolution.Tick:
                        if (increment > TimeSpan.FromMilliseconds(1) && includeTick)
                        {
                            increment = new TimeSpan(0, 0, 0, 0, 1);
                        }
                        break;
                }
            }
            return increment;
        }

    } // End FileSystem Local Feed Class:
} // End Namespace

4. BackTestingDataFeed 回归测试数据槽

namespace QuantConnect.Lean.Engine.DataFeeds
{
    /******************************************************** 
    * CLASS DEFINITIONS
    *********************************************************/
    /// <summary>
    /// Backtesting data feed extends the filesystem data feed with almost no modifications. Later this method can
    /// be used for implementing alternative sources/generation for backtesting data.
    /// 回归测试数据槽是文件系统数据槽的派生类
    /// </summary>
    public class BacktestingDataFeed : FileSystemDataFeed
    {
        /******************************************************** 
        * CLASS VARIABLES
        *********************************************************/

        /******************************************************** 
        * CLASS PROPERTIES
        *********************************************************/

        /******************************************************** 
        * CLASS CONSTRUCTOR
        *********************************************************/
        /// <summary>
        /// Pass through the backtesting datafeed to the underlying file system datafeed implementation.
        /// </summary>
        /// <param name="algorithm">Algorithm we're operating with</param>
        /// <param name="job">Algorithm worker job</param>
        public BacktestingDataFeed(IAlgorithm algorithm, BacktestNodePacket job) : base(algorithm, job)
        {
            DataFeed = DataFeedEndpoint.Backtesting;
        }
    } // End Backtesting Feed Class:
} // End Namespace


此外还有数据库数据槽DataBaseDataFeed和LiveTradingDataFeed实时交易数据槽。在这里就不在说明。

第四天:5天免费搭建个人量化平台 - 实时数据 第四天:5天免费搭建个人量化平台 - 实时数据 数据源配置及API配置 阅读详情

相关推荐

无刷电机FOC控制入门:磁场、洛伦兹力与坐标转换详解

本文深入解析无刷电机FOC(磁场定向控制)的核心原理,从电磁学基础的磁场与洛伦兹力出发,详细阐述了如何通过Clarke变换、Park变换等坐标转换技术,将三相交流解耦为直流量进行控制,最终利用SVPWM技术实现平滑、高效的电机驱动。文章旨在帮助开发者理解FOC的物理本质与数学框架,为机器人、无人机等高性能应用奠定基础。

weixin_29306011的博客 267

Datafeed非展示行情

Datafeed非展示行情

1750

怎样自己亲手搭建一个四比特加法器

如何用三极管搭建一个四比特加法器 一、前言 作为大一上学期第一个大作业,最终还是拖到了冬学期的第八周,从第七周的周六开始,一直到第八周周五上午课上展示,中间连续熬了好几天的夜,另外,白天凡是有时间都用在研究它了,最终大约是用了30个小时,完成了这个四比特加法器的“从零到一”。 最终,小组得分92,个人分数100(贡献比35%),也不枉自己投入的心血 二、器材 电阻 1 包(10kΩ×100,1kΩ×50) 三极管 1 包(9013×100,LED×6) 跳线 3 短 1.5 长 电源 1 个 USB 线

qq_34771124的博客 1万+

DataFeed模块

Backtrader中Feeds模块提供了灵活的数据加载和处理功能,支持多种数据源和格式,可以添加一个或者多个股票数据。Feed是一个数据源对象,负责向策略提供时间序列数据,如股票的开盘价、收盘价、成交量等。每个Feed对象代表一个数据源,可以是本地的CSV文件,也可以是实时的股票数据。GenericCSVData:用于加载通用的CSV格式数据。YahooFinanceData:用于从Yahoo Finance下载数据。PandasData:用于从Pandas DataFrame加载数据。

天山老妖 1836

大模型训练显存瘦身全记录:从ZeRO-3参数分区到CPU Offload的硬件协奏曲

本文详细解析了如何通过ZeRO-3参数分区与CPU/NVMe卸载技术优化大模型训练的显存使用。结合bf16混合精度与梯度累积等策略,实现在消费级GPU上高效训练百亿参数模型,显著提升硬件利用率与训练效率。

weixin_29051245的博客 435

在 PFCG 中完成 SAP Fiori 对象分配:把 Business Catalog、Space、Page 与授权一次讲透

摘要:SAP Fiori权限配置的关键在于理解Business Catalog与Space的分层授权逻辑。传统SAP GUI中直接分配事务码的方式不适用于Fiori环境,必须通过PFCG为角色分配Catalog(提供应用访问权限)和Space(控制首页展示结构)。Catalog会自动解析所需OData服务授权,而Space则决定用户在Launchpad中看到的导航布局。常见错误包括直接分配Page而非其所属Space,或仅配置Space而忽略Catalog。最佳实践是优先使用Catalog驱动授权提议,再在

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 327

数据馈送系统(Datafeed):一个面向金融数据的开源解决方案

数据馈送系统(Datafeed):一个面向金融数据的开源解决方案 Datafeed 是一款基于Python和HDF5构建的快速、可扩展的金融行情数据存储系统。该系统由IMIGU(imigu.com)在生产环境中成功运行超过一年,并且遵循Apache 2.0许可协议。它旨在提供一个高效的数据交互平台,支持从不同来源获取市场数据。 主要编程语言及环境 编程语言:主要使用 Python 2.7 开发,...

gitblog_00010的博客 921

启动launch文件时显示找不到franka_ign_ros2_control-system...如何解决?

🏆本文收录于 《全栈 Bug 调优(实战版)》 专栏。专栏聚焦真实项目中的各类疑难 Bug,从成因剖析 → 排查路径 → 解决方案 → 预防优化全链路拆解,形成一套可复用、可沉淀的实战知识体系。无论你是初入职场的开发者,还是负责复杂项目的资深工程师,都可以在这里构建一套属于自己的「问题诊断与性能调优」方法论,助你稳步进阶、放大技术价值 。

**My Coding Family** 695

数据喂食系统:金融数据的极速处理解决方案 —— Datafeed 深度探索

数据喂食系统:金融数据的极速处理解决方案 —— Datafeed 深度探索 在金融大数据的海洋中航行,精确、高效的数据处理是每位分析师和交易员的灯塔。今天,我们来探讨一款专为金融市场设计的开源项目——Datafeed。这是一套基于Python和HDF5构建的快速、可扩展的行情数据存储系统,已经通过IMIGU公司的生产环境考验超过一年时间。 项目介绍 Datafeed,正如其名,是一个旨在简化金融...

gitblog_01145的博客 467

【基于Arduino 的CNC笔式绘图仪】

该机器的结构基于我之前视频中的DIY CNC激光雕刻机,目标是以尽可能少的零件制造最简单的CNC机器。它使用 3 个 NEMA 17 步进电机进行 X、Y 和 Z 轴运动,并使用一个小伺服器进行夹持器。这款数控笔式绘图仪的大脑是一块Arduino UNO板,结合了一个数控屏蔽和三个A4988步进驱动器。在我看来,相当大的工作区域,360x280mm,以及这款笔式绘图仪可以输出的细节水平也令人印象深刻。我真的很惊讶这些图纸的好坏和精确程度,尤其是我使用0.6毫米中性笔的图纸。

vor234的博客 1499

【数据集】MODIS 的 MOD07 大气廓线产品概述及下载

MODIS 的 MOD07 大气廓线产品概述及下载

WW、forever的博客 551

数据君的量化技术-番外篇 认识 datafeeds

有感兴趣量化技术,特别是大数据、人工智能在量化中的应用的童鞋们可以关注我的公众号,脚本代码如果排版不清楚请看公众号: 馨视野(datahomex):

fish982000的博客 2423

CATIA二次开发核心技术:多实体零件自动化拆解与装配生成深度解析(零件转产品)

CATIA二次开发核心技术:多实体零件自动化拆解与装配生成深度解析

KE17RS的博客 2145

开源项目 `datafeed` 使用教程

开源项目 datafeed 使用教程 1. 项目的目录结构及介绍 datafeed/ ├── README.md ├── datafeed │ ├── __init__.py │ ├── main.py │ ├── config.py │ ├── utils.py │ └── data │ ├── sample_data.csv │ └── proces...

gitblog_01065的博客 571

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Backtrader 量化回测实践(5)—— Datafeed 增加列数据

在官网上的拓展示例是 GenericCSVData ,没有拓展pandas数据加载的示例,实际中数据库读取到pandas中,从pandas中加载到Cerebro中,需要解决pandas拓展Cerebro的数据加载的默认列。

六月闻君 1668

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Lean — 优秀好用的开源量化交易平台

Lean 是 QuantConnect 开源的一款非常强大的开源量化交易平台,可以回测或运行Python或者C#写的策略,并在代码仓库中内置了上百个C#和Python的策略算法。这个开源的...

Oner.wv的专栏 5919

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

用 LeanEngine 开发微信公众号自动回复机器人

随着微信逐渐开放的数据接口,开发者可以设置自定义的服务器地址,微信的服务端会把用户发送给公众号的消息转发到开发者自定义的服务器上,并且开发者可以调用接口进行回复,这就成就了基于微信进行二次开发的可能性。 把微信和 LeanEngine 的网站托管(Web Hosting)功能结合起来,就可以做一个几乎零成本的微信公众号自动问答机器人。 场景设定 本文的主要目的是指导开发者使用 LeanEngine...

weixin_33709219的博客 295

LeanEngine 中使用 WebSocket

一直以来 HTTP 协议都是使用「请求/响应」的模型,在 Web 应用越来越复杂的今天,这种模型的限制越来越明显。 很多场景下,我们想要服务器主动发送通知给浏览器,甚至我们想在浏览器中实现一个实时对战的网络游戏。这个时候 HTML5 规范中的 WebSocket 可以很好地满足我们的需求。 对于使用 LeanEngine 的用户,我们...

weixin_33965305的博客 218

如何获取a股level2数据接口?

在2017年底最新的交易所政策里,允许获得Datafeed牌照的信息商或者会员单位转发行情给客户,不过限定在交易所指定机房内(这个指定机房不等同于交易所机房)。另外一种牌照是Datafeed牌照,不能展示,但是可以通过接口获取,这种之前是只卖给授权信息商和会员单位(公募、券商等),一个交易所30万一年。股票Level2有2种牌照,一种是展示牌照,类似大智慧、同花顺在PC或者手机终端展示的,这种牌照是不允许数据落地,不允许接口获取。同时交易所也有了最新的政策,可以销售level2的历史数据拉。

Q_2037696191的博客 1202

TradingView 使用UDF方式 实现k线 接口的参数详细配置

tradingview开发 使用UDF方式(通用数据反馈)实现k线 因为tradingview 图表自身会去自动调用接口数据,所以需要后端需要提供以下4个接口,而且接口名称必须保持一致(目前未在源码中找到在哪修改接口名字的地方) 本文不啰嗦那么多,想看详细解释和更多配置可以查看官方文档,此处仅对前后端数据交互进行概述 api调用 接口一:Datafeed 配置数据接口 接口名称/config,传参方式:get Request:GET /config 接口返回数据: { ..

superKM的博客 4086

Lean的各个组件介绍分析

这些文件夹和文件为 Lean 量化交易平台的不同方面提供支持,从算法开发和测试到数据管理和优化。

远方雪的专栏 828

探索 LeanCloud 的 LeanEngine Node.js 演示项目:构建云应用的新范式

探索 LeanCloud 的 LeanEngine Node.js 演示项目:构建云应用的新范式 该项目是 LeanCloud 提供的一系列基于 Node.js 的 LeanEngine 示例代码库,旨在帮助开发者快速理解和使用 LeanEngine 进行云端应用程序开发。作为一个高效、易于集成的后端服务框架,LeanEngine 可以帮助你节省大量时间和精力,专注于核心业务逻辑,而不是基础设施的...

gitblog_00086的博客 551

引用:Lean方法旨在通过减少浪费和增加价值来提高生产效率

流程改进,指在原有工作流程的基础上,通过识别、分析、评估、优化和重构等方法,对流程进行改进,以达到提高工作质量、效率、降低成本、优化资源利用、保障产品安全生产等目的的过程。流程改进旨在发现和消除流程中的瓶颈、缺陷和浪费,优化和重构流程以提高工作效率、提高产品的品质、增强组织的竞争力。引用:增长黑客的方法与精益创业不同,它侧重于在验证产品的需求和解决方案后,通过快速试验和迭代来实现用户增长、激活和留存,并在此过程中寻找新的增长点。Lean方法是一种优化生产流程的方法,它通过减少浪费和优化价值流来提高效率。

[Blog][Domain] programb.blog.youkuaiyun.com 1112

探索LEAN引擎:开源算法交易的未来

LEAN引擎是一款开源的算法交易引擎,专为策略研究、回测和实时交易设计。它由QuantConnect开发,旨在与常见的数据提供商和经纪商集成,使用户能够快速部署算法交易策略。LEAN引擎的核心代码采用C#编写,但它在Linux、Mac和Windows操作系统上都能无缝运行,并支持Python 3.11和C#编写的算法。 ## 项目技术分析 LEAN引擎的设计高度模块化,允许用户在不触及其他文件...

gitblog_00354的博客 648
上一篇: 程序员的量化交易(34)--QuantConnect_Lean如何定义Indicator指标2
下一篇: 程序员的量化交易之路(36)--Lean之数据读取SubscriptionDataReader4
minimicall
博客等级 码龄18年 407粉丝 174原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值