Installing ClockworkDB ===================================== For linux, you can install ClockworkDB using the provided RPM or DEB packages. These packages will install the necessary binaries, libraries, and configuration files to get you up and running with ClockworkDB quickly. On Redhat based distributions: #. ctt-clockworkdb-support-.rpm for RPM-based distributions (e.g., CentOS, RHEL, Fedora) #. ctt-clockworkdb-.rpm for RPM-based distributions (e.g., CentOS, RHEL, Fedora) #. ctt-clockworkdb-utilities-.rpm for RPM-based distributions (e.g., CentOS, RHEL, Fedora) On Debian based distributions: #. ctt-clockworkdb-support-.deb for DEB-based distributions (e.g., Ubuntu, Debian) #. ctt-clockworkdb-.deb for DEB-based distributions (e.g., Ubuntu, Debian) #. ctt-clockworkdb-utilities-.deb for DEB-based distributions (e.g., Ubuntu, Debian) Configuring ClockworkDB ===================================== On linux, when using RPM or DEB packages, the configuration files are installed in the `/opt/ctt/etc/clockworkdb/` directory. They are configured with basic settings for a WarpDrive+ repository named "warp1" that should work for most users. Follow the simple steps below to configure your environment, and run a quick test to list the repositories available. Quick Start #. Source the `cdb-env.sh` file to set up environment variables for ClockworkDB configuration. You should add this to your shell profile or rc file (.zshrc, .bashrc, .bash_profile, etc.):: source /opt/ctt/etc/clockworkdb/cdb-env.sh #. Test that the configuration is set up correctly by running a ClockworkDB tool, such as the command line interface (CLI) tool `cdb-repositories` which lists the repositories defined in the configuration files. Output:: #> cdb-repositories /\_/\ ( o.o ) > ^ < ____ _ _ _ ____ ____ / ___| | ___ ___| | ____ _____ _ __| | _| _ \| __ ) | | | |/ _ \ / __| |/ /\ \ /\ / / _ \| '__| |/ / | | | _ \ | |___| | (_) | (__| < \ V V / (_) | | | <| |_| | |_) | \____|_|\___/ \___|_|\_\ \_/\_/ \___/|_| |_|\_\____/|____/ v1.0.48 [11:32:52] [cdb-repositories] cdb-repositories Version: 0.3.3 [11:32:52] [cdb-repositories] Repository Description Module Provider [11:32:52] [cdb-repositories] -------------------- -------------------------------------------------- --------------- [11:32:52] [cdb-repositories] warp1 WarpDrive+ Repository(2k/16k) mod_warpdrive [11:32:52] [cdb-repositories] -------------------- -------------------------------------------------- --------------- Creating and Managing Configuration Files ================================================= You can manage the configuration files for ClockworkDB using any text editor. The configuration files are XML files that define various settings and options for the ClockworkDB environment, repositories, and module providers that provide presistence logic for various backends. To generate a new backend module provider configuration file, you can use the `cdb-provider-config` command line tool, which will create a new configuration file:: Usage: cdb-provider-config #> cdb-provider-config mod_warpdrive There are a number of configuration files which are used by the various tools, utilities, and programs which interact with ClockworkDB. These configuration files are XML files and one DTD file that defines ENTITYs, essentially variables used in the XML configuration files. #. `cdb-env.sh` file. This is a shell script that sets environment variables to point to the ClockworkDB configuration directory, TOM_HOME, and adjusts the PATH and LD_LIBRARY_PATH environment variables to include the directories for tools and libraries. You should source this file before running any of the tools, utilities, or programs which interact with ClockworkDB. #. `tom-environment.dtd` file. This file defines ENTITYs, which are essentially variables that can be used in the XML configuration files. This allows for easier management of configuration values and reduces redundancy across the XML files. Entities defined in this file can be referenced in the XML configuration files using the syntax `&entity_name;`. A sample `tom-environment.dtd` file:: #. `tom-environment.xml` file. This file defines elements for logging, license, and clockworkdb environment configuration. When creating a new configuration file for a new repository, you can include this file via the directive. See the `` in the `tom-environment.xml` file for an example. A sample `tom-environment.xml` file:: #. `cdb1.xml` file. This file is included in the `tom-environment.xml` file and defines the configuration for a specific repository, in this case, a WarpDrive+ powered repository named "warp1". This file includes options for cache size, partitioning, transactions, environment settings, locking, datastore configuration, database home directory, directories for data and logs, and replication settings. You can find more information about the various configuration options in the `Module Provider Options` section of the documentation. A sample `cdb1.xml` file:: /opt/ctt/usr/data/warp1 Sample Python Programs ===================================== The Python API for ClockworkDB allows you to interact with the engine, repositories, datastores, and data using Python code. Below are some sample Python programs that demonstrate how to use the API to interact with the configured repositories and datastores in your environment. These programs can all be found in the `examples/` directory of the source code, and you can run them after installing the package and setting up your environment. List configured Repositories ----------------------------- You can use the `cdb-repositories` command line tool to list the repositories available in your environment, and then use the repository names in your Python programs to create sessions and interact with the data. The code below mimics the behavior of the `cdb-repositories` tool, however, it uses the Python API to interact with the engine and print out the repository metadata. Code:: #!/bin/env python3 # The core ClockworkDB API is in the clockworkdb.tsdb module from clockworkdb.tsdb import * if __name__ == "__main__": # The engine class is a singleton responsible for managing sessions and repositories. # You ALWAYS get an instance of the engine using Engine.Instance() e = Engine.Instance() # This asks the engine for metadata about the repositories configured in the environment, # and prints out the name, description, and module provider for each repository. for repo in e.get_repositories(): print(f"Repository Name: {repo.name()}") print(f" Description: {repo.description()}") print(f" Module: {repo.module()}") print() Output:: Repository Name: warp1 Description: WarpDrive+ Data (2k/16k) Module: mod_warpdrive List Repository Datastores ----------------------------- You can use the `cdb-datastores` command line tool to list the datastores available in a given repository, and then use the datastore names in your Python programs to interact with the data. The code below mimics the behavior of the `cdb-datastores` tool, however, it uses the Python API to interact with the engine and print out the datastore metadata for a given repository. Code:: #!/bin/env python3 from clockworkdb.tsdb import * if __name__ == "__main__": e = Engine.Instance() # get a session for the "warp1" repository, which is configured in the environment. # You can have multiple repositories configured, and you can get sessions for any of them by name. session = e.get_session("warp1") # If you pass nothing to get_session(), it will use the default repository configured in your # environment, which is often what you want. # session = e.get_session() # WarpDrive+ is embedded in the same process as your Python program, so you can get a direct # connection to the data store connection = session.get_connection() # This asks the connection for metadata about the datastores configured in the repository, # and prints out the name of each datastore. for ds in connection.get_datastores(): print(f"Datastore Name: {ds}") Output:: Datastore Name: co-insider-transactions.wdb Datastore Name: co-logo.wdb Datastore Name: co-news.wdb Datastore Name: co-peers.wdb Datastore Name: co-profile.wdb Datastore Name: fxdata.wdb Datastore Name: market-news.wdb Datastore Name: mktdata.wdb Datastore Name: normal.ann.wdb Datastore Name: normal.wdb Datastore Name: sec-master.wdb Datastore Name: stac-test.wdb Catalog a Datastore ----------------------------- You can use the `cdb-catalog` command line tool to print out the catalog of a given datastore, which includes the timeseries and vectors stored in the datastore. The code below mimics the behavior of the `cdb-catalog` tool, however, it uses the Python API. Code:: #!/bin/env python3 from clockworkdb.tsdb import * if __name__ == "__main__": # should be looking normal. Get an engine instance, get a session for the "warp1" repository, and get a connection, # and then a datastore for the "mktdata" datastore in that repository. NOTE: we open the datastore in read-only mode, # which is all we need to do to list the objects in it. e = Engine.Instance() session = e.get_session("warp1") connection = session.get_connection() datastore = connection.get_datastore("mktdata", AccessMode.READ_ONLY()) # Then we can do a regex search for all objects in that datastore and print their names. for match in datastore.regex_name_search(".*"): print(f"Object Name: {match}") Output:: Object Name: A.ADJUSTED Object Name: A.CLOSE Object Name: A.HIGH Object Name: A.LOW Object Name: A.OPEN Object Name: A.VOLUME Object Name: AA.ADJUSTED Object Name: AA.CLOSE Object Name: AA.HIGH Object Name: AA.LOW Object Name: AA.OPEN Object Name: AA.VOLUME Object Name: AACG.ADJUSTED Object Name: AACG.CLOSE Object Name: AACG.HIGH ... Object Name: ZWS.CLOSE Object Name: ZWS.HIGH Object Name: ZWS.LOW Object Name: ZWS.OPEN Object Name: ZWS.VOLUME Object Name: ZYME.ADJUSTED Object Name: ZYME.CLOSE Object Name: ZYME.HIGH Object Name: ZYME.LOW Object Name: ZYME.OPEN Object Name: ZYME.VOLUME Object Name: ZYXI.ADJUSTED Object Name: ZYXI.CLOSE Object Name: ZYXI.HIGH Object Name: ZYXI.LOW Object Name: ZYXI.OPEN Object Name: ZYXI.VOLUME Display timeseries/vector metadata ------------------------------------ You can use the `cdb-ts-meta` command line tool to print out the metadata of a given timeseries or vector, which includes information about the object such as its name, type, number of records, and other relevant metadata. The code below mimics the behavior of the `cdb-ts-meta` tool, however, it uses the Python API. Code:: #!/bin/env python3 # used to get at argv for passing in the name of the timeseries to get metadata for. # If no name is passed, it defaults to "nvda.close" import sys # The core ClockworkDB API is in the clockworkdb.tsdb module, and we also need to import # the calendars and scalar modules to get at the relevant types for timeseries metadata. from clockworkdb.tsdb import * from clockworkdb.calendars import * from clockworkdb.scalar import * if __name__ == "__main__": # normal setup to get at the datastore. We get an engine instance, then a session for the "warp1" repository, # then a connection, and then a datastore for the "mktdata" datastore in that repository. # NOTE: we open the datastore in read-only mode, which is all we need to do to get metadata about the timeseries stored in it. e = Engine.Instance() session = e.get_session("warp1") connection = session.get_connection() datastore = connection.get_datastore("mktdata", AccessMode.READ_ONLY()) ts_name = sys.argv[1] if len(sys.argv) > 1 else "nvda.close" # check to see that we have the timeseries/vector in the datastore, and if we do, get it and print out its metadata. # If not, print an error message and exit. if( not datastore.has_time_series(ts_name) ): print(f"Time series {ts_name} not found in datastore.") datastore.close() sys.exit(1) # If we have the timeseries/vector, we can get it from the datastore and print out its metadata, including its # name, type, calendar, creation and modification dates, first and last dates, and count of records. ts = datastore.get_time_series(ts_name) print(f"Time Series Name: {ts.name()}") print(f" Type: {ts.get_data_type().name()}") print(f" Calendar: {ts.get_calendar().name()}") print(f" Created: {ts.get_create_date()}") print(f" Modified: {ts.get_modify_date()}") print(f" First Date: {ts.get_first_date()}") print(f" Last Date: {ts.get_last_date()}") print(f" Count: {ts.get_last_date_int() - ts.get_first_date_int() + 1:,d}") Output (passing xom.close as argv[1]):: Time Series Name: XOM.CLOSE Type: Float Calendar: Business Created: 2024-Mar-02 21:26:58 Modified: 2026-Feb-15 13:37:18 First Date: 1970-Jan-02 Last Date: 2026-Feb-13 Count: 14,641 Display timeseries/vector data ------------------------------------ You can use the `cdb-ts` command line tool to print out the data of a given timeseries or vector, which includes the date and value of each record in the timeseries or vector. The code below mimics the behavior of the `cdb-ts` tool, however, it uses the Python API. Code:: #!/bin/env python3 # used to get at argv for passing in the name of the timeseries to get metadata for. # If no name is passed, it defaults to "nvda.close" import sys # The core ClockworkDB API is in the clockworkdb.tsdb module, and we also need to import # the calendars and scalar modules to get at the relevant types for timeseries metadata. from clockworkdb.tsdb import * from clockworkdb.calendars import * from clockworkdb.scalar import * if __name__ == "__main__": # normal setup to get at the datastore. We get an engine instance, then a session for the "warp1" repository, # then a connection, and then a datastore for the "mktdata" datastore in that repository. # NOTE: we open the datastore in read-only mode, which is all we need to do to get metadata about the timeseries stored in it. e = Engine.Instance() session = e.get_session("warp1") connection = session.get_connection() datastore = connection.get_datastore("mktdata", AccessMode.READ_ONLY()) ts_name = sys.argv[1] if len(sys.argv) > 1 else "nvda.close" # check to see that we have the timeseries/vector in the datastore, and if we do, get it and print out its metadata. # If not, print an error message and exit. if( not datastore.has_time_series(ts_name) ): print(f"Time series {ts_name} not found in datastore.") datastore.close() sys.exit(1) # If we have the timeseries/vector, we can get it from the datastore # and get the calendar for the series that will be used for date conversions. ts = datastore.get_time_series(ts_name) calendar = ts.get_calendar() # print all values in the time series. Note that the observations are returned as DatedObservation objects. print(f"Time Series Name: {ts.name()}") for obs in ts.full_range(): print(obs) # print the number of data points in the time series. print(f"\nData points: {len(ts.full_range())}\n") # print out data for a specific date range. In this case, we print out all observations for December 2025. print("\nDecember 2025 Observations:\n==============================\n") for obs in ts.range(Date(2025, 12, 1), Date(2026, 1, 1)): if obs.is_normal(): print(f"{obs.date_as_string()}, {Float(obs.observation()).value():0.2f}") # internally, timeseries and vectors just store observations. There index in the vector identifies the date, time, or # ordinal location of the observation. To get the date for a given observation, you can use the calendar associated # with the timeseries/vector, and to get the value of the observation. you can use the appropriate scalar type, in this case, Float. # The code below iterates through all the observations in the timeseries/vector. rng = range( ts.get_first_date_int(), ts.get_last_date_int() + 1) print(f"Time Series Name: {ts.name()}") for date_int in rng: # We check to see if the observation is normal, which means it has a valid value. # If it does, we get the value and print it out along with the date. if( ts.get_observation(date_int).is_normal() ): value = Float(ts.get_observation(date_int)).value() d = calendar.get_date(date_int) print(f" {d}[{date_int}]: {value:.2f}") # Numpy Arrays are supported for timeseries and vectors, and you can get a numpy array # for all values in the timeseries/vector, or for a specific date range. # NOTE: np_array(...) does no memory allocations or copying of data. # It just gives you a view into the underlying data, so it's very fast. # The numpy array will have NaNs for any missing values in the timeseries/vector, # so you will want to clean the data before using it for analysis or modeling. np_all_values = ts.np_array() print(f"\nNumpy Array All Values:\n{np_all_values}\n") # or get an numpy array for a specific date range. np_dec_2025_values = ts.np_array( calendar.get_date_int( Date(2025, 12, 1) ), calendar.get_date_int( Date(2025, 12, 31) ) ) print(f"\nNumpy Array for December 2025:\n{np_dec_2025_values}\n") # If we have the timeseries/vector, we can get it from the datastore and print out its name and data. ts = datastore.get_time_series(ts_name) # print all values in the time series. Note that the observations are returned as DatedObservation objects. print(f"Time Series Name: {ts.name()}") for obs in ts.full_range(): print(obs) print(f"\nData points: {len(ts.full_range())}\n") print("\nDecember 2025 Observations:\n==============================\n") for obs in ts.range(Date(2025, 12, 1), Date(2026, 1, 1)): if obs.is_normal(): print(f"{obs.date_as_string()}, {Float(obs.observation()).value():0.2f}") # internally, timeseries and vectors just store observations. There index in the vector identifies the date, time, or # ordinal location of the observation. To get the date for a given observation, you can use the calendar associated # with the timeseries/vector, and to get the value of the observation. you can use the appropriate scalar type, in this case, Float. # The code below iterates through all the observations in the timeseries/vector. rng = range( ts.get_first_date_int(), ts.get_last_date_int() + 1) calendar = ts.get_calendar() print(f"Time Series Name: {ts.name()}") for date_int in rng: # We check to see if the observation is normal, which means it has a valid value. # If it does, we get the value and print it out along with the date. if( ts.get_observation(date_int).is_normal() ): value = Float(ts.get_observation(date_int)).value() d = calendar.get_date(date_int) print(f" {d}[{date_int}]: {value:.2f}") Output (passing xom.close as argv[1]):: Time Series Name: XOM.CLOSE 1970-Jan-02: 1.9375 1970-Jan-05: 1.96875 1970-Jan-06: 1.96484399 1970-Jan-07: 1.953125 1970-Jan-08: 1.95703101 1970-Jan-09: 1.96484399 1970-Jan-12: 1.94921899 1970-Jan-13: 1.94140601 1970-Jan-14: 1.94531298 ... 2026-Apr-29: 154.669998 2026-Apr-30: 154.330002 2026-May-01: 152.75 2026-May-04: 153.690002 2026-May-05: 154.880005 2026-May-06: 148.690002 2026-May-07: 146.580002 2026-May-08: 144.570007 2026-May-11: 149.679993 2026-May-12: 150.630005 2026-May-13: 151.570007 Data points: 14704 December 2025 Observations: ============================== 2025-Dec-01, 116.63 2025-Dec-02, 115.38 2025-Dec-03, 117.80 2025-Dec-04, 117.14 2025-Dec-05, 116.54 2025-Dec-08, 115.98 2025-Dec-09, 118.25 2025-Dec-10, 119.54 2025-Dec-11, 119.54 2025-Dec-12, 118.82 2025-Dec-15, 117.76 2025-Dec-16, 114.68 2025-Dec-17, 117.41 2025-Dec-18, 116.54 2025-Dec-19, 116.69 2025-Dec-22, 118.15 2025-Dec-23, 119.42 2025-Dec-24, 119.22 2025-Dec-26, 119.11 2025-Dec-29, 120.53 2025-Dec-30, 120.99 2025-Dec-31, 120.34 Time Series Name: XOM.CLOSE 1970-Jan-02[31309]: 1.94 1970-Jan-05[31310]: 1.97 1970-Jan-06[31311]: 1.96 1970-Jan-07[31312]: 1.95 1970-Jan-08[31313]: 1.96 1970-Jan-09[31314]: 1.96 1970-Jan-12[31315]: 1.95 1970-Jan-13[31316]: 1.94 1970-Jan-14[31317]: 1.95 ... 2026-Apr-29[46002]: 154.67 2026-Apr-30[46003]: 154.33 2026-May-01[46004]: 152.75 2026-May-04[46005]: 153.69 2026-May-05[46006]: 154.88 2026-May-06[46007]: 148.69 2026-May-07[46008]: 146.58 2026-May-08[46009]: 144.57 2026-May-11[46010]: 149.68 2026-May-12[46011]: 150.63 2026-May-13[46012]: 151.57 Numpy Array All Values: [ 1.9375 1.96875 1.964844 ... 149.68 150.63 151.57 ] Numpy Array for December 2025: [1.166300e+02 1.153800e+02 1.178000e+02 1.171400e+02 1.165400e+02 1.159800e+02 1.182500e+02 1.195400e+02 1.195400e+02 1.188200e+02 1.177600e+02 1.146800e+02 1.174100e+02 1.165400e+02 1.166900e+02 1.181500e+02 1.194200e+02 1.192200e+02 3.402823e+38 1.191100e+02 1.205300e+02 1.209900e+02 1.203400e+02] Numpy: Load, clean, normalize, and scale data ------------------------------------ This example demonstrates how to load timeseries data from a ClockworkDB datastore exposed natively as a numpy array, clean the data by handling missing values, normalize the data, and scale the data to a specific sampling rate. This is a common workflow for preparing time series data for analysis or machine learning. Code:: #!/bin/env python3 # for command line argument parsing import argparse as opts # for progress bar during normalization from rich.progress import track # for working with numpy arrays ( division and log operations ) import numpy as np # core clockworkdb imports from clockworkdb.tsdb import * from clockworkdb.scalar import * from clockworkdb.calendars import * # parse command line arguments parser = opts.ArgumentParser(description="Load time series data, normalize it, and scale it.") parser.add_argument("--repo", type=str, default="warp1", help="The repository to read time series from.") parser.add_argument("--mktdata_ds", type=str, default="mktdata-stk", help="The datastore to read time series from.") parser.add_argument("--start-date", type=str, default="2020-01-01", help="The start date for normalization (inclusive). Format: YYYY-MM-DD") parser.add_argument("--end-date", type=str, default="2026-04-30", help="The end date for normalization (inclusive). Format: YYYY-MM-DD") args = parser.parse_args() print(f"ClockWorkDB Version: {Engine.version()}") print(f"Provider Name: {Engine.Instance().get_provider(args.repo).name()}") print(f"Provider Description: {Engine.Instance().get_provider(args.repo).description()}") print(f"Provider Version: {Engine.Instance().get_provider(args.repo).version()}") print(f"Provider ClockWorkDB Version: {Engine.Instance().get_provider(args.repo).tsdb_version()}\n") # output the configuration we're using for this run print(f"Repository: {args.repo}") print(f"Mkt Data Datastore: {args.mktdata_ds}") print(f"Start Date: {args.start_date}") print(f"End Date: {args.end_date}\n") # clockkworkdb boilerplate to get a connection to the source datastore and the target datastore. engine = Engine.Instance() session = engine.get_session(args.repo) connection = session.get_connection() mktdata_ds = connection.get_datastore(args.mktdata_ds, AccessMode.READ_ONLY()) bizCal = BusinessCalendar.Instance() start_date_int = bizCal.get_date_int(Date.from_string(args.start_date)) end_date_int = bizCal.get_date_int(Date.from_string(args.end_date)) mod_20 = (end_date_int - start_date_int + 1) % 20 if mod_20 != 0: print(f"Warning: The number of observations ({end_date_int - start_date_int + 1}) is not a multiple of 20. Adjusting start date appropriately.") start_date_int -= (20 - mod_20) print(f"Adjusted Start Date: {bizCal.get_date(start_date_int)}") # get the universe (mktdata time_series are named like "nvda.close", "nvda.volume", etc. # so we can get the universe by looking for all time series that end with ".close" and removing the ".close" suffix to get the ticker name). names = [] for n in mktdata_ds.regex_name_search('.*\\.CLOSE'): names.append(n.removesuffix(".CLOSE")) print(f"Working with {len(names)} time series.\n") # Track which time series we were able to calculate normalized values for, and which we were not # (due to insufficient data in the date range we're looking at). calcd_names = [] not_calcd_names = [] data_points = 0 for n in track(names, "Loading, normalizing, and scaling time series..."): close = mktdata_ds.get_time_series(f"{n}.close") if close.get_first_date_int() > start_date_int or close.get_last_date_int() < end_date_int: not_calcd_names.append(n) continue else: calcd_names.append(n) volume = mktdata_ds.get_time_series(f"{n}.volume") data_points += (close.get_last_date_int() - close.get_first_date_int() + 1) * 2 # close and volume data points close_np = close.np_array(start_date_int, end_date_int) volume_np = volume.np_array(start_date_int, end_date_int) # volume_np /= volume_np.mean() # normalize volume by its mean to get a relative volume measure volume_np /= 1e6 # convert volume to millions of shares to avoid overflow issues when multiplying by close price # get rid of missing values by rolling fwd previous values # you could also use ~np.isfinite(close_np) instead of close_np > 100000000000 to identify missing values missing_ixs = np.argwhere( close_np > 100000000000 ) close_np[missing_ixs] = close_np[missing_ixs -1] volume_np[missing_ixs] = volume_np[missing_ixs -1] volume_dols = (close_np * volume_np).reshape((-1, 20)).sum(axis=1) / 20 tmp = np.log(close_np[1:] / close_np[:-1]) tmp.resize( len(tmp) + 1 ) close_returns = tmp.reshape((-1, 20)).sum(axis=1) print(f"\nLoaded, normalized, and scaled {len(calcd_names)} time series with {data_points:,d} data points.\n") print("Monthly returns:") print(close_returns) print("\nAverage Daily Volume in millions of dollars Monthly:") print(volume_dols) mktdata_ds.close() Output (Runtime < 0.01 seconds on an average modern workstation):: ClockWorkDB Version: 1.0 Provider Name: mod_warpdrive Provider Description: WarpDrive+ Persistence Module Provider Version: 1.0.10 Provider ClockWorkDB Version: 1.0.43 Repository: warp1 Mkt Data Datastore: mktdata-stk Start Date: 2020-01-01 End Date: 2026-04-30 Warning: The number of observations (1652) is not a multiple of 20. Adjusting start date appropriately. Adjusted Start Date: 2019-Dec-20 Working with 4894 time series. Loading, normalizing, and scaling time series... ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 100% 0:00:00 Loaded, normalized, and scaled 3132 time series with 40,793,246 data points. Monthly returns: [ 0.08184254 -0.02369032 -0.50431496 0.26551902 -0.06252877 0.03079736 -0.01708502 -0.16425332 0.03918283 0.35254055 -0.09734881 0.14046182 0.0876473 0.06030452 -0.28792605 -0.1496893 -0.12989567 -0.02283954 0.04579985 0.13981655 -0.12821965 0.02432041 -0.1422589 -0.21511137 -0.14930749 -0.19318292 -0.08695573 -0.68053496 -0.11449037 -0.04694284 -0.07565946 0.17167412 -0.24810034 0.05273643 0.12233867 -0.22503927 0.17055102 0.21640068 -0.18335311 0.44094765 -0.09720448 -0.13871963 0.16899225 -0.08324295 0.0034659 -0.0034662 -0.17264624 0.05746407 -0.18492241 0.1163932 0.13022259 0.10536063 0.17865163 0.02451306 0.0256765 -0.17015815 -0.04775444 -0.08149295 0.00353169 0.23366374 0.05782997 0.1391127 0.02935829 0.13251074 -0.20160998 0.04962238 0.0771291 -0.17395315 -0.09156746 0.16852793 -0.12562612 0.08708417 0.06669125 0.12467743 0.07339805 0.12425767 0.00650053 0.31904253 -0.09503542 -0.01961282 0.05813603 0.06899258 0.06526507] Average Daily Volume in millions of dollars Monthly: [19.575901 23.388968 15.149574 13.7345 12.7751255 21.32802 13.927608 16.250233 10.642692 24.843687 13.538633 13.642285 23.869211 19.612621 31.677425 15.580046 11.016501 16.664085 9.474551 20.270182 12.3855295 10.663541 17.85745 18.545792 9.05739 19.802538 12.769226 14.536023 4.9035807 6.2257295 20.855188 10.080467 7.0726433 4.690911 4.8945436 2.8793113 7.803706 8.873507 5.928445 15.415558 8.140029 5.044221 5.1164327 4.879816 4.7082434 6.378849 3.7835107 4.5327444 2.2964537 2.3949752 3.085166 2.9361417 6.075511 5.2374682 6.2982416 6.525164 5.572005 5.3588 5.550838 6.544217 7.845552 8.98573 7.0092688 8.241705 5.970148 7.497822 5.371728 6.676184 8.808483 8.564287 5.1574774 5.4714994 6.679696 5.741189 7.564517 11.91343 19.819511 45.03958 20.45116 16.58244 14.527692 18.063606 16.011637 ]