{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "configuration parameters below optimize Spark configuration to the data." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "execution": { "iopub.execute_input": "2023-05-31T12:12:45.165641Z", "iopub.status.busy": "2023-05-31T12:12:45.164895Z", "iopub.status.idle": "2023-05-31T12:13:04.693194Z", "shell.execute_reply": "2023-05-31T12:13:04.693062Z", "shell.execute_reply.started": "2023-05-31 12:12:45.174688+00:00" }, "tags": [] }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "[I 2023-05-31 12:12:45,174.174 configure_magic] Magic cell payload received: {\"conf\": {\"spark.pyspark.python\": \"python3\", \"spark.pyspark.virtualenv.enabled\": \"true\", \"spark.kubernetes.executor.node.selector.node-lifecycle\": \"spot\", \"spark.pyspark.virtualenv.type\": \"native\", \"spark.pyspark.virtualenv.bin.path\": \"/usr/bin/virtualenv\", \"spark.sql.files.ignoreCorruptFiles\": \"true\", \"spark.dynamicAllocation.executorIdleTimeout\": \"18000\", \"spark.driver.memory\": \"32g\", \"spark.driver.cores\": \"32\", \"spark.driver.maxResultSize\": \"24g\", \"spark.executor.memory\": \"32g\", \"spark.network.timeout\": \"300\", \"spark.executor.cores\": \"6\", \"spark.yarn.executor.Overhead\": \"12g\", \"spark.dynamicAllocation.maxExecutors\": \"500\", \"livy.server.session.timeout\": \"24h\", \"spark.sql.shuffle.partitions\": \"15000\"}, \"proxyUser\": \"assumed-role_fdp_blitvin-Isengard\"}\n", "\n", "[I 2023-05-31 12:12:45,174.174 configure_magic] Sending request to update kernel. Please wait while the kernel will be refreshed.\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "The kernel is successfully refreshed." ] } ], "source": [ "%%configure -f\n", "{ \"conf\":{\n", " \"spark.pyspark.python\": \"python3\"\n", " ,\"spark.pyspark.virtualenv.enabled\": \"true\"\n", " ,\"spark.kubernetes.executor.node.selector.node-lifecycle\":\"spot\"\n", " ,\"spark.pyspark.virtualenv.type\":\"native\"\n", " ,\"spark.pyspark.virtualenv.bin.path\":\"/usr/bin/virtualenv\"\n", " ,\"spark.sql.files.ignoreCorruptFiles\":\"true\"\n", " ,\"spark.dynamicAllocation.executorIdleTimeout\":\"18000\"\n", " ,\"spark.driver.memory\":\"32g\"\n", " ,\"spark.driver.cores\":\"32\"\n", " ,\"spark.driver.maxResultSize\":\"24g\"\n", " ,\"spark.executor.memory\":\"32g\"\n", " ,\"spark.network.timeout\":\"300\"\n", " ,\"spark.executor.cores\":\"6\"\n", " ,\"spark.yarn.executor.Overhead\":\"12g\"\n", " ,\"spark.dynamicAllocation.maxExecutors\":\"500\"\n", " ,\"livy.server.session.timeout\":\"24h\"\n", " ,\"spark.sql.shuffle.partitions\":\"15000\"\n", " }\n", "} " ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "execution": { "iopub.execute_input": "2023-05-31T12:14:57.493175Z", "iopub.status.busy": "2023-05-31T12:14:57.492575Z", "iopub.status.idle": "2023-05-31T12:14:57.498422Z", "shell.execute_reply": "2023-05-31T12:14:57.497211Z", "shell.execute_reply.started": "2023-05-31T12:14:57.493139Z" }, "tags": [] }, "outputs": [], "source": [ "# ,\"spark.sql.adaptive.enabled\":\"true\"\n", "# ,\"spark.sql.adaptive.coalescePartitions.enabled\":\"true\"\n", "# ,\"spark.sql.adaptive.coalescePartitions.initialPartitionNum\":\"1000\"" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "https://towardsdatascience.com/basics-of-apache-spark-configuration-settings-ca4faff40d45\n", "https://luminousmen.com/post/spark-tips-partition-tuning\n", "https://sparkbyexamples.com/pyspark/pyspark-repartition-vs-partitionby/" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2023-05-31T12:14:58.566941Z", "iopub.status.busy": "2023-05-31T12:14:58.566364Z", "iopub.status.idle": "2023-05-31T12:15:00.902252Z", "shell.execute_reply": "2023-05-31T12:15:00.901081Z", "shell.execute_reply.started": "2023-05-31T12:14:58.566906Z" }, "tags": [] }, "outputs": [], "source": [ "import pandas as pd\n", "import pyarrow\n", "import s3fs\n", "import fsspec\n", "import matplotlib\n", "import matplotlib.pyplot as plt\n", "import pyspark.sql.functions as py_f\n", "from pyspark.sql.window import Window" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2023-05-31T12:15:00.904718Z", "iopub.status.busy": "2023-05-31T12:15:00.904173Z", "iopub.status.idle": "2023-05-31T12:15:01.069293Z", "shell.execute_reply": "2023-05-31T12:15:01.067974Z", "shell.execute_reply.started": "2023-05-31T12:15:00.904682Z" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "3.3.0-amzn-1 \n", "\n", "\n", "('spark.kubernetes.executor.pod.allowlistFile', '/etc/spark/conf/executor-pod-template-pod-allowlist.txt')\n", "('spark.eventLog.enabled', 'true')\n", "('spark.eventLog.dir', 'file:///var/log/spark/apps')\n", "('spark.kubernetes.memoryOverheadFactor', '0.4')\n", "('spark.kubernetes.executor.podTemplateContainerName', 'spark-kubernetes-executor')\n", "('spark.dynamicAllocation.maxExecutors', '500')\n", "('spark.kubernetes.driverEnv.HTTP2_DISABLE', 'true')\n", "('spark.sql.parquet.output.committer.class', 'com.amazon.emr.committer.EmrOptimizedSparkSqlParquetOutputCommitter')\n", "('spark.driver.cores', '32')\n", "('spark.blacklist.decommissioning.timeout', '1h')\n", "('spark.kubernetes.driver.node.selector.node-lifecycle', 'on-demand')\n", "('spark.kubernetes.driver.label.kernel_id', 'fe646132-3324-45e3-a0b1-486d99cb4b6a')\n", "('spark.hadoop.dynamodb.customAWSCredentialsProvider', 'com.amazonaws.auth.WebIdentityTokenCredentialsProvider')\n", "('spark.kubernetes.driver.container.allowlistFile', '/etc/spark/conf/driver-pod-template-container-allowlist.txt')\n", "('spark.sql.emr.internal.extensions', 'com.amazonaws.emr.spark.EmrSparkSessionExtensions')\n", "('spark.dynamicAllocation.executorAllocationRatio', '1')\n", "('spark.kubernetes.driver.podTemplateContainerName', 'spark-kubernetes-driver')\n", "('spark.kubernetes.namespace', 'adxuseast1emr')\n", "('spark.history.fs.logDirectory', 'file:///var/log/spark/apps')\n", "('spark.kubernetes.executor.selector.node.role', 'notebook')\n", "('spark.kubernetes.executor.node.selector.spark-role', 'executor')\n", "('spark.yarn.heterogeneousExecutors.enabled', 'false')\n", "('spark.driver.extraLibraryPath', '/etc/hadoop/conf:/usr/lib/hadoop/lib/native:/usr/lib/hadoop-lzo/lib/native:/docker/usr/lib/hadoop/lib/native:/docker/usr/lib/hadoop-lzo/lib/native')\n", "('spark.pyspark.python', 'python3')\n", "('spark.kubernetes.container.image.pullPolicy', 'Always')\n", "('spark.kubernetes.submitInDriver', 'true')\n", "('spark.kubernetes.executor.podNamePrefix', 'kfe646132-3324-45e3-a0b1-486d99cb4b6a-660ebf8871b9dedf')\n", "('spark.kubernetes.driver.node.selector.spark-role', 'driver')\n", "('spark.driver.defaultJavaOptions', \"-XX:OnOutOfMemoryError='kill -9 %p' -XX:+UseParallelGC -XX:InitiatingHeapOccupancyPercent=70\")\n", "('spark.executor.defaultJavaOptions', \"-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseParallelGC -XX:InitiatingHeapOccupancyPercent=70 -XX:OnOutOfMemoryError='kill -9 %p'\")\n", "('spark.executor.extraClassPath', '/etc/hadoop/conf:/usr/lib/hadoop-lzo/lib/*:/usr/lib/hadoop/hadoop-aws.jar:/usr/share/aws/aws-java-sdk/*:/usr/share/aws/emr/emrfs/conf:/usr/share/aws/emr/emrfs/lib/*:/usr/share/aws/emr/emrfs/auxlib/*:/usr/share/aws/emr/goodies/lib/emr-spark-goodies.jar:/usr/share/aws/emr/security/conf:/usr/share/aws/emr/security/lib/*:/usr/share/aws/hmclient/lib/aws-glue-datacatalog-spark-client.jar:/usr/share/java/Hive-JSON-Serde/hive-openx-serde.jar:/usr/share/aws/sagemaker-spark-sdk/lib/sagemaker-spark-sdk.jar:/usr/share/aws/emr/s3select/lib/emr-s3-select-spark-connector.jar:/docker/usr/lib/hadoop-lzo/lib/*:/docker/usr/lib/hadoop/hadoop-aws.jar:/docker/usr/share/aws/aws-java-sdk/*:/docker/usr/share/aws/emr/emrfs/conf:/docker/usr/share/aws/emr/emrfs/lib/*:/docker/usr/share/aws/emr/emrfs/auxlib/*:/docker/usr/share/aws/emr/goodies/lib/emr-spark-goodies.jar:/docker/usr/share/aws/emr/security/conf:/docker/usr/share/aws/emr/security/lib/*:/docker/usr/share/aws/hmclient/lib/aws-glue-datacatalog-spark-client.jar:/docker/usr/share/java/Hive-JSON-Serde/hive-openx-serde.jar:/docker/usr/share/aws/sagemaker-spark-sdk/lib/sagemaker-spark-sdk.jar:/docker/usr/share/aws/emr/s3select/lib/emr-s3-select-spark-connector.jar')\n", "('spark.driver.extraJavaOptions', \"-XX:+IgnoreUnrecognizedVMOptions --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/sun.nio.cs=ALL-UNNAMED --add-opens=java.base/sun.security.action=ALL-UNNAMED --add-opens=java.base/sun.util.calendar=ALL-UNNAMED --add-opens=java.security.jgss/sun.security.krb5=ALL-UNNAMED -XX:OnOutOfMemoryError='kill -9 %p' -XX:+UseParallelGC -XX:InitiatingHeapOccupancyPercent=70\")\n", "('spark.driver.bindAddress', '10.0.119.242')\n", "('spark.app.startTime', '1685535185487')\n", "('spark.executor.id', 'driver')\n", "('spark.kubernetes.driver.podTemplateValidation.enabled', 'true')\n", "('spark.hadoop.hive.metastore.client.factory.class', 'com.amazonaws.glue.catalog.metastore.AWSGlueDataCatalogHiveClientFactory')\n", "('spark.kubernetes.allocation.batch.size', '5')\n", "('spark.driver.port', '7078')\n", "('spark.executor.memory', '32g')\n", "('spark.executorEnv.LOG_CONTEXT_WITH_PATH_SEPARATOR', '')\n", "('spark.decommissioning.timeout.threshold', '20')\n", "('spark.sql.catalogImplementation', 'hive')\n", "('spark.stage.attempt.ignoreOnDecommissionFetchFailure', 'true')\n", "('spark.kubernetes.driver.container.image', '614393260192.dkr.ecr.us-east-1.amazonaws.com/cdk-hnb659fds-container-assets-614393260192-us-east-1:eaafe31dc2e70d39e0b2871e0e6af73ceb184a3978386368425caf36de957b6a')\n", "('spark.pyspark.virtualenv.enabled', 'true')\n", "('spark.kubernetes.driver.pod.allowlistFile', '/etc/spark/conf/driver-pod-template-pod-allowlist.txt')\n", "('spark.kubernetes.driver.label.component', 'kernel')\n", "('spark.dynamicAllocation.shuffleTracking.timeout', '300s')\n", "('spark.dynamicAllocation.executorIdleTimeout', '18000')\n", "('spark.sql.files.ignoreCorruptFiles', 'true')\n", "('spark.kubernetes.executor.label.app', 'enterprise-gateway')\n", "('spark.app.name', 'kfe646132-3324-45e3-a0b1-486d99cb4b6a')\n", "('spark.pyspark.virtualenv.bin.path', '/usr/bin/virtualenv')\n", "('spark.kubernetes.client.dependency.propagation', 'false')\n", "('spark.yarn.executor.Overhead', '12g')\n", "('spark.hadoop.fs.s3.getObject.initialSocketTimeoutMilliseconds', '2000')\n", "('spark.hadoop.mapreduce.fileoutputcommitter.algorithm.version.emr_internal_use_only.EmrFileSystem', '2')\n", "('spark.authenticate', 'true')\n", "('spark.shuffle.service.enabled', 'false')\n", "('spark.kubernetes.executor.podTemplateFile', '/opt/spark/pod-template/pod-spec-template.yml')\n", "('spark.kubernetes.driver.request.cores', '0.5')\n", "('spark.driver.memory', '32g')\n", "('spark.kubernetes.authenticate.driver.serviceAccountName', 'emr-containers-sa-spark-jeg-kernel-614393260192-1290uyahhbialm60icp0xrrxth1kcn9j6a76dmo6zat8qk68y99ok5m2qt')\n", "('spark.driver.host', 'spark-40308c8871b9b8db-driver-svc.adxuseast1emr.svc')\n", "('spark.pyspark.virtualenv.type', 'native')\n", "('spark.kubernetes.executor.podTemplateValidation.enabled', 'true')\n", "('spark.kubernetes.pyspark.pythonVersion', '3')\n", "('spark.hadoop.fs.defaultFS', 'file:///')\n", "('spark.app.submitTime', '1685535182271')\n", "('spark.kubernetes.driver.pod.name', 'kfe646132-3324-45e3-a0b1-486d99cb4b6a-4f55c78871b9aee2-driver')\n", "('spark.kubernetes.executor.node.selector.node-lifecycle', 'spot')\n", "('spark.serializer.objectStreamReset', '100')\n", "('spark.app.id', 'spark-41c56dec16b348eab9dd44ea60a8981a')\n", "('spark.kubernetes.submission.waitAppCompletion', 'false')\n", "('spark.kubernetes.executor.label.emr-containers.amazonaws.com/kernel-type', 'PySpark')\n", "('spark.submit.deployMode', 'client')\n", "('spark.master', 'k8s://https://172.20.0.1:443')\n", "('spark.kubernetes.executor.container.allowlistFile', '/etc/spark/conf/executor-pod-template-container-allowlist.txt')\n", "('spark.sql.parquet.fs.optimized.committer.optimization-enabled', 'true')\n", "('spark.dynamicAllocation.shuffleTracking.enabled', 'true')\n", "('spark.hadoop.mapreduce.fileoutputcommitter.cleanup-failures.ignored.emr_internal_use_only.EmrFileSystem', 'true')\n", "('spark.kubernetes.executor.label.component', 'worker')\n", "('spark.driver.maxResultSize', '24g')\n", "('spark.network.timeout', '300')\n", "('spark.history.ui.port', '18080')\n", "('spark.driver.blockManager.port', '7079')\n", "('spark.hadoop.fs.s3.customAWSCredentialsProvider', 'com.amazonaws.auth.WebIdentityTokenCredentialsProvider')\n", "('spark.kubernetes.driver.selector.node.role', 'notebook')\n", "('spark.kubernetes.executor.container.image', '614393260192.dkr.ecr.us-east-1.amazonaws.com/cdk-hnb659fds-container-assets-614393260192-us-east-1:eaafe31dc2e70d39e0b2871e0e6af73ceb184a3978386368425caf36de957b6a')\n", "('spark.executor.extraJavaOptions', \"-XX:+IgnoreUnrecognizedVMOptions --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/sun.nio.cs=ALL-UNNAMED --add-opens=java.base/sun.security.action=ALL-UNNAMED --add-opens=java.base/sun.util.calendar=ALL-UNNAMED --add-opens=java.security.jgss/sun.security.krb5=ALL-UNNAMED -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+UseParallelGC -XX:InitiatingHeapOccupancyPercent=70 -XX:OnOutOfMemoryError='kill -9 %p'\")\n", "('spark.driver.extraClassPath', '/etc/hadoop/conf:/usr/lib/hadoop-lzo/lib/*:/usr/lib/hadoop/hadoop-aws.jar:/usr/share/aws/aws-java-sdk/*:/usr/share/aws/emr/emrfs/conf:/usr/share/aws/emr/emrfs/lib/*:/usr/share/aws/emr/emrfs/auxlib/*:/usr/share/aws/emr/goodies/lib/emr-spark-goodies.jar:/usr/share/aws/emr/security/conf:/usr/share/aws/emr/security/lib/*:/usr/share/aws/hmclient/lib/aws-glue-datacatalog-spark-client.jar:/usr/share/java/Hive-JSON-Serde/hive-openx-serde.jar:/usr/share/aws/sagemaker-spark-sdk/lib/sagemaker-spark-sdk.jar:/usr/share/aws/emr/s3select/lib/emr-s3-select-spark-connector.jar:/docker/usr/lib/hadoop-lzo/lib/*:/docker/usr/lib/hadoop/hadoop-aws.jar:/docker/usr/share/aws/aws-java-sdk/*:/docker/usr/share/aws/emr/emrfs/conf:/docker/usr/share/aws/emr/emrfs/lib/*:/docker/usr/share/aws/emr/emrfs/auxlib/*:/docker/usr/share/aws/emr/goodies/lib/emr-spark-goodies.jar:/docker/usr/share/aws/emr/security/conf:/docker/usr/share/aws/emr/security/lib/*:/docker/usr/share/aws/hmclient/lib/aws-glue-datacatalog-spark-client.jar:/docker/usr/share/java/Hive-JSON-Serde/hive-openx-serde.jar:/docker/usr/share/aws/sagemaker-spark-sdk/lib/sagemaker-spark-sdk.jar:/docker/usr/share/aws/emr/s3select/lib/emr-s3-select-spark-connector.jar')\n", "('spark.resourceManager.cleanupExpiredHost', 'true')\n", "('spark.kubernetes.executor.label.kernel_id', 'fe646132-3324-45e3-a0b1-486d99cb4b6a')\n", "('spark.kubernetes.driver.label.app', 'enterprise-gateway')\n", "('spark.files.fetchFailure.unRegisterOutputOnHost', 'true')\n", "('spark.kubernetes.driver.label.emr-containers.amazonaws.com/kernel-type', 'PySpark')\n", "('spark.executor.extraLibraryPath', '/etc/hadoop/conf:/usr/lib/hadoop/lib/native:/usr/lib/hadoop-lzo/lib/native:/docker/usr/lib/hadoop/lib/native:/docker/usr/lib/hadoop-lzo/lib/native')\n", "('spark.eventLog.logBlockUpdates.enabled', 'true')\n", "('spark.kubernetes.resource.type', 'python')\n", "('spark.kubernetes.authenticate.executor.serviceAccountName', 'emr-containers-sa-spark-jeg-kernel-614393260192-1290uyahhbialm60icp0xrrxth1kcn9j6a76dmo6zat8qk68y99ok5m2qt')\n", "('spark.executor.cores', '6')\n", "('spark.rdd.compress', 'True')\n", "('spark.sql.shuffle.partitions', '15000')\n", "('spark.dynamicAllocation.minExecutors', '0')\n", "('spark.submit.pyFiles', '')\n", "('spark.dynamicAllocation.enabled', 'true')\n", "('spark.kubernetes.executor.request.cores', '3.5')\n", "('spark.blacklist.decommissioning.enabled', 'true')\n" ] } ], "source": [ "print(spark.version,\"\\n\\n\")\n", "configurations = spark.sparkContext.getConf().getAll()\n", "for conf in configurations:\n", " print(conf)\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We analyzed the data published over the SIP feeds (CTA : CQS / CTS and UTP : UQDF / UTDF) and the depth of the book feeds of the 16 lit venues under the Reg. NMS system to see the impact of these proposed reforms on the quality of the market. \n", "\n", "Definitions \n", "Quoted spread = (bid - ask) / midpoint \n", "Spread - bid - ask \n", "\n", "Results - \n", "1)\tNumber of current odd - lot trades within each bucket. & number of trades in each bucket. \n", " a)\tGraphic concentration \n", " b)\t– look for rationale & see if it is – \n", "2)\tAverage round-lot and odd-lot quoted spreads across each bucket \n", " a)\tmatrix \n", " b)\tHeat map \n", " c)\tHour of the day ? \n", "3)\tEffect on market data- \n", " a)\tAnticipated increase in MD volumes - counts, etc\n", " b)\tNumber of direct feed updates where top of the book is an odd - lot\n", "4)\tCase study around AMZN stock split - \n", " a)\tRound lot spreads for AMZN per exchange - when high priced before the split\n", " b)\tOdd-lot spreads for AMZN per exchange - after the split. \n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "\n" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2023-05-31T12:15:09.747449Z", "iopub.status.busy": "2023-05-31T12:15:09.746925Z", "iopub.status.idle": "2023-05-31T12:27:11.842970Z", "shell.execute_reply": "2023-05-31T12:27:11.841813Z", "shell.execute_reply.started": "2023-05-31T12:15:09.747414Z" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "s3://maystreetdata/feeds_norm/partition_scheme_experiments_7/mstnorm_parquet_0_5_0\n", "mt_oddlot_prepped\n", "mt_roundlot_bbo_prepped\n", "mt_roundlot_nbbo_prepped\n", "mt_oddlot\n", "mt_roundlot_bbo\n", "mt_roundlot_nbbo\n" ] } ], "source": [ "class MtRoundLot():\n", " def __init__(self,part_experiment_id, is_debug,debug_symbol='AMZN'):\n", " self.s3_dir_root=\"s3://maystreetdata/feeds_norm/mstnorm_parquet_0_5_0\"\n", " self.s3_dir_root_prepped=\"s3://maystreetdata/feeds_norm/partition_scheme_experiments_7/mstnorm_parquet_0_5_0/\"\n", " self.s3_dir_partition_experiments =f\"s3://maystreetdata/feeds_norm/partition_scheme_experiments_{part_experiment_id}/mstnorm_parquet_0_5_0\"\n", " self.s3_dir_root_results =f\"s3://maystreetdata/analysis/\"\n", " self.tables={\"mt_roundlot_bbo\":{\"tables\":[f\"{self.s3_dir_root}/mt=bbo_quote/\"]},\n", " \"mt_roundlot_nbbo\":{\"tables\":[f\"{self.s3_dir_root}/mt=nbbo_quote/\"]},\n", " \"mt_oddlot\":{\"tables\":[f\"{self.s3_dir_root}/mt=aggregated_price_update/\"]},\n", " \"mt_trade\":{\"tables\":[f\"{self.s3_dir_root}/mt=trade/\"]}\n", " }\n", " self.tables_prepped={\"mt_roundlot_bbo\":{\"tables\":[f\"{self.s3_dir_root_prepped}/mt_roundlot_bbo.parquet\"]},\n", " \"mt_roundlot_nbbo\":{\"tables\":[f\"{self.s3_dir_root_prepped}/mt_roundlot_nbbo.parquet\"]},\n", " \"mt_oddlot\":{\"tables\":[f\"{self.s3_dir_root_prepped}/mt_oddlot.parquet\"]}\n", " }\n", " self.raw_df ={}\n", " self.raw_df_prepped ={}\n", " self.stats_df ={}\n", " self.data_validation ={}\n", " self.column_map={}\n", " self.joined_df={}\n", " self.column_map['mt_oddlot']={ \n", " 'ask':'AskPrice_1'\n", " ,'bid':'BidPrice_1'\n", " ,'timestamp':'LastExchangeTimestamp'\n", " ,'seq_number':'LastSequenceNumber'\n", " ,'BidQuantity':'BidQuantity_1'\n", " ,'AskQuantity':'AskQuantity_1'\n", " ,'partition_by':['Product','FeedType',\"Feed\",\"dt\",'f','MarketParticipant','is_trading_hours','hour_est']\n", " }\n", " self.column_map['mt_roundlot_bbo']={\n", " 'ask':'AskPrice'\n", " ,'bid':'BidPrice'\n", " ,'timestamp':'ExchangeTimestamp'\n", " ,'seq_number':'SequenceNumber'\n", " ,'BidQuantity':'BidQuantity'\n", " ,'AskQuantity':'AskQuantity'\n", " ,'partition_by':['Product','FeedType',\"Feed\",\"dt\",'f','is_trading_hours','hour_est']\n", " }\n", " self.column_map['mt_roundlot_nbbo']={\n", " 'ask':'AskPrice'\n", " ,'bid':'BidPrice'\n", " ,'timestamp':'ExchangeTimestamp'\n", " ,'seq_number':'SequenceNumber'\n", " ,'BidQuantity':'BidQuantity'\n", " ,'AskQuantity':'AskQuantity'\n", " ,'partition_by':['Product','FeedType',\"Feed\",\"dt\",'f','is_trading_hours','hour_est']\n", " }\n", " self.round_factor=0.333333333333\n", " self.is_debug=is_debug\n", " self.debug_symbol=debug_symbol\n", " print(self.s3_dir_partition_experiments)\n", " def set_data_prepped(self,data_label):\n", " col_map=self.column_map.get(data_label)\n", " data_files = self.tables_prepped.get(data_label).get('tables')\n", " data_df=None\n", " for one_file in data_files:\n", " if self.is_debug:\n", " one_data_df=spark.read.parquet(one_file).where(f\"Product=='{self.debug_symbol}'\")\n", " else:\n", " one_data_df=spark.read.parquet(one_file)\n", " if data_df is None:\n", " data_df=one_data_df\n", " else:\n", " data_df=data_df.union(one_data_df)\n", " self.raw_df_prepped[f\"{data_label}\"]=data_df\n", " def set_data(self,data_label, is_raw=False):\n", " col_map=self.column_map.get(data_label)\n", " data_files = self.tables.get(data_label).get('tables')\n", " data_df=None\n", " for one_file in data_files:\n", " feed_filters = self.tables.get(data_label).get('feeds',None)\n", " path_parts= one_file.split(\"/\")\n", " feed_type=path_parts[len(path_parts)-2:len(path_parts)-1][0]\n", " if feed_filters is not None:\n", " filter_string='\"'+'\",\"'.join(feed_filters)+'\"'\n", " one_data_df = spark.read.parquet(one_file).filter(f'Feed in ({filter_string})') \n", " else:\n", " one_data_df = spark.read.parquet(one_file)\n", " if self.is_debug:\n", " one_data_df=one_data_df.where(f\"Product=='{self.debug_symbol}'\")\n", " if 'f' not in one_data_df.columns:\n", " one_data_df = one_data_df.withColumn('f', py_f.col(\"Feed\"))\n", " if data_df is None:\n", " data_df=one_data_df\n", " else:\n", " data_df=data_df.union(one_data_df)\n", " data_df = data_df.withColumn('FeedType', py_f.lit(feed_type))\\\n", " .select('FeedType','Feed','f','Product',col_map['bid'],col_map['ask'],col_map['timestamp'],col_map['BidQuantity'],col_map['AskQuantity'])\\\n", " .groupBy('FeedType','Feed','f','Product',col_map['timestamp']).agg(\n", " py_f.round(py_f.max(col_map['bid']),3).alias(f'best_bid_{data_label}')\n", " ,py_f.round(py_f.min(col_map['ask']),3).alias(f'best_ask_{data_label}')\n", " ,py_f.round(py_f.max(col_map['BidQuantity']),3).alias(f'bid_quantity_{data_label}')\n", " ,py_f.round(py_f.max(col_map['AskQuantity']),3).alias(f'ask_quantity_{data_label}')\n", " ).withColumnRenamed('FeedType', f'FeedType_{data_label}')\\\n", " .withColumnRenamed('Feed', f'Feed_{data_label}')\\\n", " .withColumnRenamed(col_map['timestamp'], f'exchange_timestamp_{data_label}')\\\n", " .withColumnRenamed('f', f'f_{data_label}')\\\n", " .withColumn(f\"mid_{data_label}\",(py_f.col(f'best_ask_{data_label}')+py_f.col(f'best_bid_{data_label}'))/py_f.lit(2))\\\n", " .withColumn(f\"bid_ask_{data_label}\",(py_f.col(f'best_ask_{data_label}')-py_f.col(f'best_bid_{data_label}'))/py_f.col(f\"mid_{data_label}\")) \\\n", " .withColumn(f'timestamp_ts_utc_{data_label}',py_f.from_unixtime(py_f.col(f'exchange_timestamp_{data_label}')/1000/1000/1000))\\\n", " .withColumn(f'timestamp_ts_est_{data_label}',py_f.from_utc_timestamp((py_f.from_unixtime(py_f.col(f'exchange_timestamp_{data_label}')/1000/1000/1000)),'America/New_York'))\\\n", " .withColumn(f'time_ts_est_{data_label}',py_f.date_format(f'timestamp_ts_est_{data_label}', 'HH:mm:ss'))\\\n", " .withColumn(f'hour_est_{data_label}',py_f.date_format(f'timestamp_ts_est_{data_label}', 'HH'))\\\n", " .withColumn(f'date_est_{data_label}',py_f.date_format(f'timestamp_ts_est_{data_label}', 'yyyy-MM-dd'))\\\n", " .withColumn(f'is_trading_hours', ((py_f.col(f'timestamp_ts_est_{data_label}')>=py_f.lit('09:30:00'))&(py_f.col(f'timestamp_ts_est_{data_label}')<=py_f.lit('15:59:00'))))\n", " \n", " part_by = [f'FeedType_{data_label}',\"Product\"]\n", " self.raw_df[f\"{data_label}\"]=data_df.repartition(15000,*part_by)\n", " def dv_universe(self):\n", " dv_key='universe_check'\n", " self.data_validation[dv_key]={}\n", " for one_key in self.raw_df.keys():\n", " one_df = self.raw_df.get(one_key)\n", " col_name = f\"{one_key}_ticker_count\"\n", " curr_count = one_df.agg(py_f.countDistinct(\"Product\").alias(col_name)).collect()\n", " curr_count = [i.__getitem__(col_name) for i in curr_count][0]\n", " self.data_validation[dv_key][one_key]=curr_count\n", " \n", " def dv_ts_unique(self):\n", " dv_key='ts_unique_check'\n", " self.data_validation[dv_key]={}\n", " for one_key in self.raw_df.keys():\n", " one_df = self.raw_df.get(one_key)\n", " col_map=self.column_map.get(one_key)\n", " ts_field=col_map.get('timestamp')\n", " seq_field=col_map.get('seq_number')\n", " count_alias,countDistinct_alias = f'count_{ts_field}',f'countDistinct_{ts_field}'\n", " uniq_ts_check = one_df.groupBy(col_map.get('partition_by')).agg(\n", " py_f.count(ts_field).alias(count_alias),py_f.countDistinct(ts_field,seq_field).alias(countDistinct_alias)\n", " ).where(f'{count_alias}>{countDistinct_alias}').count()\n", " self.data_validation[dv_key][one_key]=uniq_ts_check\n", " def set_volume_ptile(self):\n", " by_prod_feed=self.raw_df_prepped[\"mt_roundlot_nbbo\"].groupBy('Product').count().orderBy('Product')\n", " by_prod_feed=by_prod_feed.select(\"Product\",'count', \n", " py_f.round((py_f.floor(py_f.percent_rank().over( Window.partitionBy().orderBy(by_prod_feed['count']))/py_f.lit(self.round_factor))*py_f.lit(self.round_factor)),1).alias(\"update_count_pctrank\"))\n", " by_prod_feed=by_prod_feed.withColumn('volume_level',py_f.when(py_f.col('update_count_pctrank')==0.0,'low')\\\n", " .otherwise(py_f.when(py_f.col('update_count_pctrank')==0.3,'moderate').otherwise('high'))).cache()\n", " #by_prod_feed.groupBy('update_count_pctrank').count()\n", " self.volume_rank_df = by_prod_feed\n", "\n", " def set_common_universe(self):\n", " bbo_nbbo_cols = ['Product','Feed','dt','f','bidask_spread_timew_avg','data_count','is_trading_hours','hour_est']\n", " df1=self.stats_df['mt_roundlot_bbo_stats_agg'].select(bbo_nbbo_cols)\\\n", " .withColumnRenamed('bidask_spread_timew_avg',f'bidask_spread_timew_avg_bbo_roundlot').withColumnRenamed('data_count',f'data_count_bbo_roundlot')\n", " df2=self.stats_df['mt_roundlot_nbbo_stats_agg'].select(bbo_nbbo_cols)\\\n", " .withColumnRenamed('bidask_spread_timew_avg',f'bidask_spread_timew_avg_nbbo_roundlot').withColumnRenamed('data_count',f'data_count_nbbo_roundlot')\n", " temp_df = df1.join(df2\n", " ,(df1.Product==df2.Product)\n", " & (df1.Feed==df2.Feed)\n", " & (df1.dt==df2.dt)\n", " & (df1.f==df2.f)\n", " & (df1.is_trading_hours==df2.is_trading_hours)\n", " & (df1.hour_est==df2.hour_est)\n", " ).drop(df2.Product).drop(df2.Feed).drop(df2.dt).drop(df2.f).drop(df2.is_trading_hours).drop(df2.hour_est)\n", "\n", " odd_lot_cols = ['Product','dt','bidask_spread_timew_avg','FeedType','Feed','f','data_count','is_trading_hours','hour_est']\n", " df3 = self.stats_df['mt_oddlot_stats_agg'].select(odd_lot_cols)\\\n", " .withColumnRenamed('bidask_spread_timew_avg',f'bidask_spread_timew_avg_oddlot')\\\n", " .withColumnRenamed('data_count',f'data_count_oddlot')\\\n", " .withColumnRenamed('Feed',f'Feed_oddlot')\\\n", " .withColumnRenamed('f',f'f_oddlot')\n", " final_df=temp_df.join(df3\n", " ,(temp_df.Product==df3.Product)\n", " & (temp_df.dt==df3.dt)\n", " & (temp_df.is_trading_hours==df3.is_trading_hours)\n", " & (temp_df.hour_est==df3.hour_est)\n", " ).drop(df3.Product).drop(df3.dt).drop(temp_df.is_trading_hours).drop(temp_df.hour_est)\n", " volume_rank_df = self.volume_rank_df.select('Product','update_count_pctrank')\n", " final_df=final_df.join(volume_rank_df\n", " ,(volume_rank_df.Product==final_df.Product)).drop(volume_rank_df.Product)\n", " self.stats_df['all_by_symbol_feed_date']=final_df.cache()\n", " \n", " def calc_trade_stats(self):\n", " trades_df= spark.read.parquet('s3://maystreetdata/feeds_norm/mstnorm_parquet_0_5_0/mt=trade/')\n", " trades_df=trades_df.withColumn('is_odd_lot',py_f.when(py_f.col(\"Quantity\")=py_f.lit('09:30:00')) & (py_f.col('time_est')<=py_f.lit('15:59:00'))))\\\n", " .withColumn('avg_price', (py_f.col(f'best_bid_{dl1}')\n", " +py_f.col(f'best_ask_{dl1}')\n", " +py_f.col(f'best_bid_{dl2}')\n", " +py_f.col(f'best_ask_{dl2}'))/4)\\\n", " .withColumn('price_bucket', py_f.when((py_f.col('avg_price')>=0) & (py_f.col('avg_price')<250),'00000_00250').otherwise(\\\n", " py_f.when((py_f.col('avg_price')>=250) & (py_f.col('avg_price')<1000),'00250_01000').otherwise(\\\n", " py_f.when((py_f.col('avg_price')>=1000) & (py_f.col('avg_price')<10000),'01000_10000').otherwise('10000_99999'))\n", " ))\n", " \n", " prev_window = Window.partitionBy(*[part_cols]).orderBy(py_f.col('exchange_timestamp'))\n", " joined_df = joined_df.withColumn(\"prev_exchange_timestamp\", py_f.lag(py_f.col('exchange_timestamp')).over(prev_window))\n", " joined_df = joined_df.withColumn(\"diff_exchange_timestamp\",joined_df.exchange_timestamp-joined_df.prev_exchange_timestamp)\n", " joined_df = joined_df.withColumn(f\"bidask_timeweight_{dl1}\",joined_df[f'bid_ask_{dl1}']*joined_df.diff_exchange_timestamp) \n", " joined_df = joined_df.withColumn(f\"bidask_timeweight_{dl2}\",joined_df[f'bid_ask_{dl2}']*joined_df.diff_exchange_timestamp) \n", " #df_vol_rank=self.volume_rank_df.drop('update_count_pctrank').drop('count')\n", " #joined_df = df_vol_rank.join(py_f.broadcast(joined_df),(df_vol_rank.Product==joined_df.Product)).drop(df_vol_rank.Product)\n", " self.stats_df[f'joined_df_{dl1}_{dl2}']=joined_df\n", " joined_df_stats_by_symbol=joined_df.groupBy('Product',f'Feed_{dl2}',f'f_{dl2}','is_trading_hours','hour_est','price_bucket').agg(\n", " py_f.sum(py_f.col('diff_exchange_timestamp')).alias('diff_exchange_timestamp_sum')\n", " ,py_f.sum(py_f.col(f'bidask_timeweight_{dl1}')).alias(f'bidask_timeweight_{dl1}_sum')\n", " ,py_f.sum(py_f.col(f'bidask_timeweight_{dl2}')).alias(f'bidask_timeweight_{dl2}_sum')\n", " ).withColumn(f'bid_ask_tw_{dl1}',py_f.col(f'bidask_timeweight_{dl1}_sum')/py_f.col('diff_exchange_timestamp_sum'))\\\n", " .withColumn(f'bid_ask_tw_{dl2}',py_f.col(f'bidask_timeweight_{dl2}_sum')/py_f.col('diff_exchange_timestamp_sum'))\\\n", " .orderBy('Product',f'Feed_{dl2}',f'f_{dl2}','is_trading_hours').cache()\n", " self.stats_df[f'joined_df_stats_by_symbol_{dl1}_{dl2}']=joined_df_stats_by_symbol\n", " \n", " joined_df_stats_by_trading_hour=joined_df_stats_by_symbol.groupBy(f'Feed_{dl2}',f'f_{dl2}','is_trading_hours','hour_est')\\\n", " .agg(\n", " py_f.mean(py_f.col(f'bid_ask_tw_{dl1}'))\n", " ,py_f.mean(py_f.col(f'bid_ask_tw_{dl2}'))\n", " ,py_f.count(py_f.col(f'bid_ask_tw_{dl1}'))\n", " ,py_f.count(py_f.col(f'bid_ask_tw_{dl2}'))\n", " )\n", " self.stats_df[f'joined_df_stats_by_trading_hour_{dl1}_{dl2}']=joined_df_stats_by_trading_hour\n", " \n", " self.stats_df[f'2_spread_by_price_bucket_{dl1}_{dl2}']=self.stats_df[f'joined_df_stats_by_symbol_{dl1}_{dl2}']\\\n", " .where(\"is_trading_hours==True\").groupBy('price_bucket')\\\n", " .agg(py_f.mean(f'bid_ask_tw_{dl1}')\\\n", " .alias(f'spread_mean_{dl1}'),py_f.mean(f'bid_ask_tw_{dl2}').alias(f'spread_mean_{dl2}'))\n", " \n", " def add_price_bucket_bbo(self):\n", " roundlot_bbo=self.raw_df.get('mt_roundlot_bbo')\n", " roundlot_bbo =roundlot_bbo.withColumn('avg_price', (py_f.col(f'best_bid_mt_roundlot_bbo')+py_f.col(f'best_ask_mt_roundlot_bbo'))/2)\\\n", " .withColumn('price_bucket', py_f.when((py_f.col('avg_price')>=0) & (py_f.col('avg_price')<250),'00000_00250').otherwise(\\\n", " py_f.when((py_f.col('avg_price')>=250) & (py_f.col('avg_price')<1000),'00250_01000').otherwise(\\\n", " py_f.when((py_f.col('avg_price')>=1000) & (py_f.col('avg_price')<10000),'01000_10000').otherwise('10000_99999'))\n", " ))\n", " self.raw_df['mt_roundlot_bbo']=roundlot_bbo\n", " \n", " def calc_AMZN_before_after_split(self):\n", " \n", " start_date = '2022-06-01'\n", " split_date = '2022-06-06'\n", " end_date = '2022-06-08'\n", " \n", " roundlot_bbo=self.raw_df.get('mt_roundlot_bbo')\\\n", " .where(\"Product=='AMZN'\")\\\n", " .where(f\"date_est_mt_roundlot_bbo >= '{start_date}'\")\\\n", " .where(f\"date_est_mt_roundlot_bbo <= '{end_date}'\")\\\n", " .withColumn('is_odd_lot',py_f.when((py_f.col('bid_quantity_mt_roundlot_bbo')<=100) | (py_f.col('ask_quantity_mt_roundlot_bbo')<=100),'odd lot').otherwise('round lot'))\n", " roundlot_stats_pd=roundlot_bbo.groupBy('price_bucket','is_odd_lot','date_est_mt_roundlot_bbo').agg(py_f.count('exchange_timestamp_mt_roundlot_bbo').alias(\"message_count\")).toPandas()\n", " roundlot_stats_pivot=pd.pivot_table(roundlot_stats_pd,values=\"message_count\",columns=[\"is_odd_lot\"],index=[\"date_est_mt_roundlot_bbo\"])\n", " roundlot_stats_pivot['total']=roundlot_stats_pivot.sum(axis=1)\n", " roundlot_stats_pivot['oddlot_pct']=roundlot_stats_pivot['odd lot']/roundlot_stats_pivot['total']\n", " self.stats_df[f\"3A_AMZN_split\"]=roundlot_stats_pivot\n", " \n", " def calc_oddlot_percent_per_price_bucket(self):\n", " roundlot_bbo=self.raw_df.get('mt_roundlot_bbo')\n", " roundlot_bbo=roundlot_bbo.withColumn('is_odd_lot',py_f.when((py_f.col('bid_quantity_mt_roundlot_bbo')<=100) | (py_f.col('ask_quantity_mt_roundlot_bbo')<=100),'odd lot').otherwise('round lot'))\n", " roundlot_stats = roundlot_bbo.groupBy('price_bucket','is_odd_lot').agg(py_f.count('is_odd_lot').alias('message_count'))\n", " roundlot_stats_pd = roundlot_stats.toPandas()\n", " odd_vs_roundlot_pct=pd.pivot_table(roundlot_stats_pd, values=\"message_count\",index=\"price_bucket\",columns=\"is_odd_lot\")\n", " odd_vs_roundlot_pct=pd.pivot_table(roundlot_stats_pd, values=\"message_count\",index=\"price_bucket\",columns=\"is_odd_lot\")\n", " odd_vs_roundlot_pct['total']=odd_vs_roundlot_pct.sum(axis=1)\n", " odd_vs_roundlot_pct['odd lot pct']=odd_vs_roundlot_pct['odd lot']/odd_vs_roundlot_pct['total']\n", " self.stats_df[f\"3_pct_trading_oddlot_per_price_bucket\"]=odd_vs_roundlot_pct\n", " \n", " def calc_timew_spread(self,data_label):\n", " col_map=self.column_map.get(data_label)\n", " l_df = self.raw_df.get(data_label)\n", " l_df = l_df.withColumn('timestamp_ts_utc',py_f.from_unixtime(py_f.col(col_map.get('timestamp'))/1000/1000/1000))\\\n", " .withColumn('timestamp_ts_est',py_f.from_utc_timestamp((py_f.from_unixtime(py_f.col(col_map.get('timestamp'))/1000/1000/1000)),'America/New_York'))\\\n", " .withColumn('time_est', py_f.date_format('timestamp_ts_est', 'HH:mm:ss'))\\\n", " .withColumn('hour_est', py_f.date_format('timestamp_ts_est', 'HH'))\\\n", " .withColumn('is_trading_hours', ((py_f.col('time_est')>=py_f.lit('09:30:00'))&(py_f.col('time_est')<=py_f.lit('15:59:00'))))\n", " l_df = l_df.withColumn(\"bid_ask\",(py_f.col(col_map.get('ask'))-py_f.col(col_map.get('bid')))/py_f.col(col_map.get('bid')) )\n", " prev_window = Window.partitionBy(*col_map.get('partition_by')).\\\n", " orderBy(py_f.col(col_map.get('timestamp')),py_f.col(col_map.get('seq_number')),l_df.bid_ask.desc())\n", " l_df = l_df.withColumn(\"next_LastReceiptTimestamp\", py_f.lead(py_f.col(col_map.get('timestamp'))).over(prev_window))\n", " l_df = l_df.withColumn(\"diff_LastReceiptTimestamp\",py_f.col(col_map.get('timestamp'))-l_df.next_LastReceiptTimestamp)\n", " l_df = l_df.withColumn(\"bidask_timeweight\",l_df.bid_ask*l_df.diff_LastReceiptTimestamp)\n", " bid_ask_agg= l_df.where('diff_LastReceiptTimestamp is not null and bid_ask<100').groupby(*col_map.get('partition_by')).\\\n", " agg(py_f.sum('diff_LastReceiptTimestamp').alias('time_sum'),\n", " py_f.sum('bidask_timeweight').alias('bidask_timeweight_sum'),\n", " py_f.count(py_f.lit(1)).alias('data_count'))\n", " bid_ask_agg=bid_ask_agg.withColumn(\"bidask_spread_timew_avg\",bid_ask_agg.bidask_timeweight_sum/bid_ask_agg.time_sum) \n", " self.stats_df[f\"{data_label}_stats_intermediate\"]=l_df\n", " self.stats_df[f\"{data_label}_stats_agg\"]=bid_ask_agg\n", " self.stats_df[f\"{data_label}_stats_agg_final\"]=bid_ask_agg.agg(py_f.mean(bid_ask_agg.bidask_spread_timew_avg).alias('bidask_mean_timew'),\n", " py_f.expr('percentile(bidask_spread_timew_avg, array(0.5))').alias('bidask_median_timew'),\n", " py_f.sum(bid_ask_agg.data_count).alias('data_count'))\n", " \n", "\n", "def main(exp_label,is_debug,debug_symbol='AMZN'): \n", " mt_roundlot=MtRoundLot(exp_label,is_debug,debug_symbol) \n", " print('mt_oddlot_prepped')\n", " mt_roundlot.set_data_prepped(\"mt_oddlot\")\n", " print('mt_roundlot_bbo_prepped')\n", " mt_roundlot.set_data_prepped(\"mt_roundlot_bbo\")\n", " print('mt_roundlot_nbbo_prepped')\n", " mt_roundlot.set_data_prepped(\"mt_roundlot_nbbo\")\n", " print('mt_oddlot')\n", " mt_roundlot.set_data(\"mt_oddlot\")\n", " print('mt_roundlot_bbo')\n", " mt_roundlot.set_data(\"mt_roundlot_bbo\")\n", " print('mt_roundlot_nbbo')\n", " mt_roundlot.set_data(\"mt_roundlot_nbbo\")\n", " mt_roundlot.join_dfs('mt_oddlot','mt_roundlot_bbo')\n", " mt_roundlot.join_dfs('mt_oddlot','mt_roundlot_nbbo')\n", " mt_roundlot.calc_timew_spread_paired(\"mt_oddlot\",\"mt_roundlot_bbo\")\n", " mt_roundlot.calc_timew_spread_paired(\"mt_oddlot\",\"mt_roundlot_nbbo\")\n", " mt_roundlot.add_price_bucket_bbo()\n", " mt_roundlot.calc_oddlot_percent_per_price_bucket()\n", " mt_roundlot.calc_AMZN_before_after_split()\n", " mt_roundlot.calc_trade_stats()\n", " return(mt_roundlot)\n", "mt_roundlot = main(7,True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Lets inspect data first" ] }, { "cell_type": "code", "execution_count": 55, "metadata": { "execution": { "iopub.execute_input": "2023-05-31T14:31:51.892397Z", "iopub.status.busy": "2023-05-31T14:31:51.891638Z", "iopub.status.idle": "2023-05-31T14:31:51.897451Z", "shell.execute_reply": "2023-05-31T14:31:51.896412Z", "shell.execute_reply.started": "2023-05-31T14:31:51.892360Z" }, "tags": [] }, "outputs": [], "source": [ "captions=[\"1.Odd-lot trading in 2022 continues to be a major component of US trading\"\n", " ,\"2.Odd-lot vs. Round-lot quoted spread\"\n", " ,\"3.Higher share prices increase odd-lot trading\"\n", " ,\"3a.AMZN pre/post split on June-6-2022\"\n", " ]" ] }, { "cell_type": "code", "execution_count": 24, "metadata": { "execution": { "iopub.execute_input": "2023-05-31T12:46:35.567663Z", "iopub.status.busy": "2023-05-31T12:46:35.566909Z", "iopub.status.idle": "2023-05-31T12:46:35.578798Z", "shell.execute_reply": "2023-05-31T12:46:35.577688Z", "shell.execute_reply.started": "2023-05-31T12:46:35.567626Z" }, "tags": [] }, "outputs": [ { "data": { "text/html": [ "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Experimentation findings: 1.Odd-lot trading in 2022 continues to be a major component of US trading
 Oddlot shares trading(%)Oddlot $ volume(%)Oddlot trade count(%)
label   
all US equity securities 8% 17% 57%
all US equity on-exchange 4% 9% 53%
all US equity off-exchange (dark pool) 89% 78% 41%
\n" ], "text/plain": [ "" ] }, "execution_count": 24, "metadata": {}, "output_type": "execute_result" } ], "source": [ "mt_roundlot.stats_df[\"1_display_all\"].style.set_caption(f\"Experimentation findings: {captions[0]}\").format({\"Oddlot shares trading(%)\": \"{:20,.0f}%\", \n", " \"Oddlot $ volume(%)\": \"{:20,.0f}%\", \n", " \"Oddlot trade count(%)\": \"{:20,.0f}%\"\n", " }).set_table_styles([{\n", " 'selector': 'caption',\n", " 'props': [\n", " ('color', 'blue'),\n", " ('font-size', '14px')\n", " ]\n", "}])" ] }, { "cell_type": "code", "execution_count": 35, "metadata": { "execution": { "iopub.execute_input": "2023-05-31T12:59:09.479812Z", "iopub.status.busy": "2023-05-31T12:59:09.479089Z", "iopub.status.idle": "2023-05-31T12:59:09.493795Z", "shell.execute_reply": "2023-05-31T12:59:09.492717Z", "shell.execute_reply.started": "2023-05-31T12:59:09.479776Z" }, "tags": [] }, "outputs": [ { "data": { "text/html": [ "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Experimentation findings: 2.Odd-lot vs. Round-lot quoted spread
price_bucketspread_mean_mt_oddlotspread_mean_mt_roundlot_bboprice_improvement
00000_002500.53%1.43%168.06%
00250_01000386.34%445.19%15.23%
01000_100000.90%3.01%235.73%
10000_999991.48%0.00%-99.99%
\n" ], "text/plain": [ "" ] }, "execution_count": 35, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#oddlot_bbo=mt_roundlot.stats_df['2_spread_by_price_bucket_mt_oddlot_mt_roundlot_bbo'].toPandas().sort_values(\"price_bucket\")\n", "oddlot_bbo['price_improvement']=((oddlot_bbo[\"spread_mean_mt_roundlot_bbo\"]/oddlot_bbo[\"spread_mean_mt_oddlot\"])-1)*1\n", "oddlot_bbo.sort_values(\"price_bucket\").style.set_caption(f\"Experimentation findings: {captions[1]}\").format(\n", " {\"spread_mean_mt_oddlot\": \"{:,.2%}\", \n", " \"spread_mean_mt_roundlot_bbo\": \"{:,.2%}\", \n", " \"price_improvement\": \"{:,.2%}\"\n", " }).set_table_styles([{\n", " 'selector': 'caption',\n", " 'props': [\n", " ('color', 'blue'),\n", " ('font-size', '14px')\n", " ]\n", "}]).hide_index()" ] }, { "cell_type": "code", "execution_count": 52, "metadata": { "execution": { "iopub.execute_input": "2023-05-31T14:27:38.151127Z", "iopub.status.busy": "2023-05-31T14:27:38.150468Z", "iopub.status.idle": "2023-05-31T14:27:38.323265Z", "shell.execute_reply": "2023-05-31T14:27:38.322218Z", "shell.execute_reply.started": "2023-05-31T14:27:38.151091Z" }, "tags": [] }, "outputs": [ { "data": { "text/html": [ "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Experimentation findings: 3.Higher share prices increase odd-lot trading
is_odd_lotodd lotround lottotalodd lot pct
price_bucket    
00000_0025048,908,368.0012,000,912.0060,909,280.0080.30%
00250_01000695.00nan695.00100.00%
01000_100004,083,944.0099,250.004,183,194.0097.63%
10000_9999995,030.0078,764.00173,794.0054.68%
\n" ], "text/plain": [ "" ] }, "execution_count": 52, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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GQHqsN27dzu93rVrl9q0aXPZZaFTp076v//7v3IFQJKCgoLUvHlzZWVlqXXr1k5frVq1uuyMu3btcvz3xYsXlZGRofDwcElS06ZNVVhYqKKiIscxP12XpFOnTo6LUSvTq1cvbdy4US+++KJmzZrl2N6tWzcdPnxYYWFh5b6HH4qaj4+PYyYJQPWgjAB1WE5OjuLj45WZmak33nhDf/3rXzVhwoTLPj8uLk52u13333+/9uzZo08//VT/+Mc/lJmZKUmaNm2aEhMTtWDBAh07dkwff/yxXn31Vc2ZM+ey3yMpKUlr167V0aNHNX78eJ05c0ajR4+WJEVERKhBgwZ65plndPz4ca1atUorVqxwOj8hIUFvvPGGEhISdOTIEX388ccVznT07t1bGzZs0LRp0xyLoI0fP16nT59WTEyMPvroIx0/flybNm3SqFGjHAUkLCxMu3fvVnZ2tgoKCnjsO1AdTF+0AqB63HLLLdYjjzxijRs3zvL397caN25sPfPMM44LWn+4sPPHfnoBq2VZ1oEDB6wBAwZYDRo0sBo1amT16dPHOn78uGP/66+/bnXp0sXy8fGxGjdubPXt29d69913fzbfD++1atUqq1evXpaPj4/Vvn1768MPP3Q6bu3atVbr1q2t+vXrW3feeaf1yiuvWD/9q+udd95xZGjSpIl1zz33OPb99PvcunWrdfXVV1sLFiywLMuyjh07Zt19991WYGCgVb9+fatdu3bWo48+6vh9yszMtG688Uarfv36liTr888//9nvDUDV2CzrJx/IAqgT+vXrpy5durAUOgC3x8c0AADAKMoIgGrx4osvqmHDhhV+DRw40HQ8AG6Ej2kAVIvTp0/r9OnTFe6rX7++WrRoUcOJALgryggAADCKj2kAAIBRlBEAAGAUZQQAABhFGQEAAEZRRgAAgFGUEQAAYBRlBAAAGEUZAQAARv0/OAvCHmffphoAAAAASUVORK5CYII=\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "oddlot_price_per_bucket=mt_roundlot.stats_df['3_pct_trading_oddlot_per_price_bucket']#.toPandas()#.sort_values(\"price_bucket\")\n", "\n", "oddlot_price_per_bucket['odd lot pct'].plot.bar()\n", "oddlot_price_per_bucket.sort_values(\"price_bucket\").style.set_caption(f\"Experimentation findings: {captions[2]}\").format(\n", " {\"round lot\": \"{:,.2f}\", \n", " \"odd lot\": \"{:,.2f}\", \n", " \"total\": \"{:,.2f}\", \n", " \"odd lot pct\": \"{:,.2%}\"\n", " }).set_table_styles([{\n", " 'selector': 'caption',\n", " 'props': [\n", " ('color', 'blue'),\n", " ('font-size', '14px')\n", " ]\n", "}])" ] }, { "cell_type": "code", "execution_count": 72, "metadata": { "execution": { "iopub.execute_input": "2023-05-31T14:46:57.420669Z", "iopub.status.busy": "2023-05-31T14:46:57.420056Z", "iopub.status.idle": "2023-05-31T14:46:57.672465Z", "shell.execute_reply": "2023-05-31T14:46:57.671353Z", "shell.execute_reply.started": "2023-05-31T14:46:57.420633Z" }, "tags": [] }, "outputs": [ { "data": { "text/html": [ "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Experimentation findings: 3a.AMZN pre/post split on June-6-2022
is_odd_lotodd lotround lottotaloddlot_pctroundlot_pct
date_est_mt_roundlot_bbo     
2022-06-01795,461.0027,818.50823,279.5096.62%3.38%
2022-06-02710,242.5025,398.50735,641.0096.55%3.45%
2022-06-03575,635.5017,762.00593,397.5097.01%2.99%
2022-06-06771,919.00357,590.001,129,509.0068.34%31.66%
2022-06-07819,418.50210,796.001,030,214.5079.54%20.46%
2022-06-08718,777.00174,090.00892,867.0080.50%19.50%
\n" ], "text/plain": [ "" ] }, "execution_count": 72, "metadata": {}, "output_type": "execute_result" }, { "data": { "image/png": "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\n", "text/plain": [ "
" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "AMZN_pre_post_split=mt_roundlot.stats_df['3A_AMZN_split']#.toPandas()#.sort_values(\"price_bucket\")\n", "AMZN_pre_post_split[\"roundlot_pct\"]=1-AMZN_pre_post_split[\"oddlot_pct\"]\n", "(AMZN_pre_post_split[['oddlot_pct','roundlot_pct']]*100).plot(kind=\"bar\", ylabel='Percent(%)',stacked=True,figsize=(7,3))\n", "AMZN_pre_post_split.style.set_caption(f\"Experimentation findings: {captions[3]}\").format(\n", " {\"round lot\": \"{:,.2f}\", \n", " \"odd lot\": \"{:,.2f}\", \n", " \"total\": \"{:,.2f}\", \n", " \"oddlot_pct\": \"{:,.2%}\", \n", " \"roundlot_pct\": \"{:,.2%}\"\n", " }).set_table_styles([{\n", " 'selector': 'caption',\n", " 'props': [\n", " ('color', 'blue'),\n", " ('font-size', '14px')\n", " ]\n", "}])" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "PySpark (Kubernetes)", "language": "python", "name": "spark_python_kubernetes" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.16" } }, "nbformat": 4, "nbformat_minor": 4 }