├── TODO ├── deploy ├── app.py ├── cdk.json ├── requirements.txt └── setup.py ├── env.sh └── ibc_docker ├── Dockerfile ├── build.sh ├── ecr_push.sh ├── init └── xvfb ├── jts.ini ├── root ├── config_template.ini ├── ibcStart_docker.py ├── start.sh └── updateSecrets.py ├── run.sh └── run_interactive.sh /TODO: -------------------------------------------------------------------------------- 1 | add tests 2 | enable container health check 3 | add fargate auto-deployment to VPC 4 | enable getting streaming quotes and pushing to Kinesis Firehose 5 | 6 | -------------------------------------------------------------------------------- /deploy/app.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from aws_cdk import ( 4 | aws_ec2 as ec2, 5 | aws_ecs as ecs, 6 | aws_ecr as ecr, 7 | aws_ecs_patterns as ecs_patterns, 8 | aws_ssm as ssm, 9 | aws_secretsmanager as secretsmanger, 10 | aws_iam as iam, 11 | aws_servicediscovery as servicediscovery, 12 | aws_logs as logs, 13 | aws_kinesis as kinesis, 14 | aws_kinesisfirehose as firehose, 15 | aws_s3 as s3, 16 | cdk, 17 | ) 18 | 19 | 20 | class IBC(cdk.Stack): 21 | def __init__( 22 | self, 23 | scope: cdk.Construct, 24 | id: str, 25 | name: str, 26 | vpc_name: str, 27 | security_group_name: str, 28 | secrets_path: str = "/ibc/paper/", 29 | trading_mode: str = "paper", 30 | **kwargs 31 | ) -> None: 32 | super().__init__(scope, id, *kwargs) 33 | 34 | # TODO: Create Log Group 35 | 36 | # Create a cluster 37 | vpc = ec2.Vpc.from_lookup(self, "vpc", vpc_name=vpc_name) 38 | 39 | privateSubnets = vpc.private_subnets 40 | 41 | cluster = ecs.Cluster(self, "cluster", vpc=vpc) 42 | # TODO: check for namespace before adding below. This is failing on stack updates. 43 | cluster.add_default_cloud_map_namespace(name="private") 44 | 45 | task = ecs.FargateTaskDefinition(self, "task", cpu="512", memory_mi_b="1024") 46 | 47 | # Add SSM Permissions to IAM Role 48 | SSM_ACTIONS = ["ssm:GetParametersByPath", "kms:Decrypt"] 49 | SSM_RESOURCES = [ 50 | "arn:aws:kms:*:*:alias/aws/ssm", 51 | "arn:aws:ssm:*:*:parameter{}*".format(secrets_path), 52 | ] 53 | ssmPolicy = iam.PolicyStatement(iam.PolicyStatementEffect.Allow) 54 | for action in SSM_ACTIONS: 55 | ssmPolicy.add_action(action) 56 | for resource in SSM_RESOURCES: 57 | ssmPolicy.add_resource(resource) 58 | task.add_to_task_role_policy(ssmPolicy) 59 | 60 | ibcRepo = ecr.Repository.from_repository_name(self, "container_repo", "ibc") 61 | 62 | ibcImage = ecs.ContainerImage.from_ecr_repository(ibcRepo, "latest") 63 | 64 | # TODO: Add to Existing Hierarchal Logger, add log_group argument with ref to it 65 | ibcLogger = ecs.AwsLogDriver(self, "logger", stream_prefix=name) 66 | 67 | connectionLossMetric = logs.MetricFilter( 68 | self, 69 | "connectionLossMetric", 70 | filter_pattern=logs.FilterPattern.literal("ERROR ?110 ?130"), 71 | log_group=ibcLogger.log_group, 72 | metric_name="ib_connection_loss", 73 | metric_namespace=name, 74 | ) 75 | 76 | newContainerMetric = logs.MetricFilter( 77 | self, 78 | "newContainerMetric", 79 | filter_pattern=logs.FilterPattern.literal( 80 | "Starting virtual X frame buffer" 81 | ), 82 | log_group=ibcLogger.log_group, 83 | metric_name="new_container", 84 | metric_namespace=name, 85 | ) 86 | 87 | kinesisFirehoseBucketActions = [ 88 | "s3:AbortMultipartUpload", 89 | "s3:GetBucketLocation", 90 | "s3:GetObject", 91 | "s3:ListBucket", 92 | "s3:ListBucketMultipartUploads", 93 | ] 94 | 95 | kinesisFirehoseBucket = s3.Bucket(self, "firehoseBucket") 96 | 97 | kinesisFirehoseBucketPolicy = iam.PolicyStatement( 98 | iam.PolicyStatementEffect.Allow 99 | ) 100 | for action in kinesisFirehoseBucketActions: 101 | kinesisFirehoseBucketPolicy.add_action(action) 102 | for resource in [ 103 | kinesisFirehoseBucket.bucket_arn, 104 | kinesisFirehoseBucket.bucket_arn + "/*", 105 | ]: 106 | kinesisFirehoseBucketPolicy.add_resource(resource) 107 | 108 | kinesisFirehoseBucketRole = iam.Role( 109 | self, 110 | "kinesisFirehoseBucketRole", 111 | assumed_by=iam.ServicePrincipal("firehose.amazonaws.com"), 112 | path="/service/" + name + "/", 113 | ) 114 | kinesisFirehoseBucketRole.add_to_policy(kinesisFirehoseBucketPolicy) 115 | 116 | kinesisFirehose = firehose.CfnDeliveryStream( 117 | self, 118 | "firehose", 119 | delivery_stream_name=name, 120 | delivery_stream_type="DirectPut", 121 | s3_destination_configuration={ 122 | "bucketArn": kinesisFirehoseBucket.bucket_arn, 123 | "bufferingHints": {"intervalInSeconds": 10 * 60, "sizeInMBs": 16}, 124 | "compressionFormat": "GZIP", 125 | "roleArn": kinesisFirehoseBucketRole.role_arn, 126 | }, 127 | ) 128 | 129 | # Add Firehose Permissions to Task IAM Role 130 | FIREHOSE_ACTIONS = ["firehose:PutRecord", "firehose:PutRecordBatch"] 131 | firehosePolicy = iam.PolicyStatement(iam.PolicyStatementEffect.Allow) 132 | for action in FIREHOSE_ACTIONS: 133 | firehosePolicy.add_action(action) 134 | firehosePolicy.add_resource(kinesisFirehose.delivery_stream_arn) 135 | task.add_to_task_role_policy(firehosePolicy) 136 | 137 | environment = { 138 | "SECRETS_PATH": secrets_path, 139 | "TWS_LIVE_PAPER": trading_mode, 140 | "FIREHOSE_STREAM_NAME": kinesisFirehose.delivery_stream_name, 141 | } 142 | 143 | ibcContainer = ecs.ContainerDefinition( 144 | self, 145 | "container", 146 | task_definition=task, 147 | image=ibcImage, 148 | environment=environment, 149 | logging=ibcLogger, 150 | essential=True, 151 | ) 152 | 153 | securityGroup = ec2.SecurityGroup.from_security_group_id( 154 | self, "task_security_group", security_group_id=security_group_name 155 | ) 156 | 157 | ibcService = ecs.FargateService( 158 | self, 159 | "fargate_service", 160 | cluster=cluster, 161 | task_definition=task, 162 | assign_public_ip=False, 163 | desired_count=1, 164 | security_group=securityGroup, 165 | service_discovery_options=ecs.ServiceDiscoveryOptions(name=name), 166 | service_name=name, 167 | vpc_subnets=privateSubnets, 168 | ) 169 | 170 | 171 | app = cdk.App() 172 | IBC( 173 | app, 174 | "ibc-live", 175 | "ibc-live", 176 | secrets_path="/ibc/live/", 177 | trading_mode="live", 178 | vpc_name="sandbox-VPC", 179 | security_group_name="sg-2cc6a145", 180 | ) 181 | IBC( 182 | app, 183 | "ibc-paper", 184 | "ibc-paper", 185 | secrets_path="/ibc/paper/", 186 | trading_mode="paper", 187 | vpc_name="sandbox-VPC", 188 | security_group_name="sg-2cc6a145", 189 | ) 190 | app.run() 191 | -------------------------------------------------------------------------------- /deploy/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "python3 app.py" 3 | } 4 | -------------------------------------------------------------------------------- /deploy/requirements.txt: -------------------------------------------------------------------------------- 1 | aws-cdk.cdk 2 | aws-cdk.aws_ec2 3 | aws-cdk.aws_ecs 4 | aws-cdk.aws_ssm 5 | aws-cdk.aws_secretsmanager 6 | aws-cdk.aws_ecs_patterns 7 | aws-cdk.aws_iam 8 | aws-cdk.aws_servicediscovery 9 | aws-cdk.aws_logs 10 | aws_cdk.aws_kinesis 11 | aws_cdk.aws_kinesisfirehose 12 | aws_cdk.aws_s3 13 | -------------------------------------------------------------------------------- /deploy/setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | 4 | with open("README.md") as fp: 5 | long_description = fp.read() 6 | 7 | 8 | setuptools.setup( 9 | name="deploy", 10 | version="0.0.1", 11 | 12 | description="An empty CDK Python app", 13 | long_description=long_description, 14 | long_description_content_type="text/markdown", 15 | 16 | author="author", 17 | 18 | package_dir={"": "deploy"}, 19 | packages=setuptools.find_packages(where="deploy"), 20 | 21 | install_requires=[ 22 | "aws-cdk.cdk", 23 | ], 24 | 25 | python_requires=">=3.6", 26 | 27 | classifiers=[ 28 | "Development Status :: 4 - Beta", 29 | 30 | "Intended Audience :: Developers", 31 | 32 | "License :: OSI Approved :: Apache Software License", 33 | 34 | "Programming Language :: JavaScript", 35 | "Programming Language :: Python :: 3 :: Only", 36 | "Programming Language :: Python :: 3.6", 37 | "Programming Language :: Python :: 3.7", 38 | "Programming Language :: Python :: 3.8", 39 | 40 | "Topic :: Software Development :: Code Generators", 41 | "Topic :: Utilities", 42 | 43 | "Typing :: Typed", 44 | ], 45 | ) 46 | -------------------------------------------------------------------------------- /env.sh: -------------------------------------------------------------------------------- 1 | export IB_PASSWORD=$(aws ssm get-parameter --name ib_password --with-decryption | jq -r ".Parameter.Value") 2 | export IB_USER=$(aws ssm get-parameter --name ib_user --with-decryption | jq -r ".Parameter.Value") 3 | export TWS_LIVE_PAPER=$(aws ssm get-parameter --name ib_mode --with-decryption | jq -r ".Parameter.Value") 4 | 5 | -------------------------------------------------------------------------------- /ibc_docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:18.04 2 | MAINTAINER Ryan Hoium (canada4663@gmail.com) 3 | 4 | ENV DEBIAN_FRONTEND noninteractive 5 | 6 | RUN apt-get update && apt-get install -y locales \ 7 | && localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 8 | RUN apt-get install -y tzdata 9 | RUN ln -fs /usr/share/zoneinfo/America/New_York /etc/localtime 10 | RUN dpkg-reconfigure --frontend noninteractive tzdata 11 | 12 | ENV LANG en_US.utf8 13 | 14 | RUN echo '2' | apt-get install -y \ 15 | unzip \ 16 | curl \ 17 | socat \ 18 | xvfb \ 19 | libxtst6 \ 20 | libxrender1 \ 21 | libxi6 22 | 23 | RUN apt-get install -y python3-pip python3-dev \ 24 | && cd /usr/local/bin \ 25 | && ln -s /usr/bin/python3 python \ 26 | && ln -s /usr/bin/pip3 pip \ 27 | #&& pip3 install --upgrade pip \ 28 | && pip install boto3 \ 29 | && pip install ssm-parameter-store 30 | 31 | # Set env vars for IBG/IBC 32 | ENV IBG_VERSION=972-stable \ 33 | IBC_VERSION=3.7.5 \ 34 | IBC_INI=/root/config.ini \ 35 | IBC_PATH=/opt/ibc \ 36 | TWS_PATH=/root/Jts \ 37 | TWS_CONFIG_PATH=/root/Jts \ 38 | LOG_PATH=/opt/ibc/logs 39 | 40 | # Install IBG 41 | RUN curl --fail --output /tmp/ibgateway-standalone-linux-x64.sh https://s3.amazonaws.com/ib-gateway/ibgateway-${IBG_VERSION}-standalone-linux-x64.sh \ 42 | && chmod u+x /tmp/ibgateway-standalone-linux-x64.sh \ 43 | && echo 'n' | sh /tmp/ibgateway-standalone-linux-x64.sh \ 44 | && rm -f /tmp/ibgateway-standalone-linux-x64.sh 45 | 46 | # Install IBC 47 | RUN curl --fail --silent --location --output /tmp/IBC.zip https://github.com/ibcalpha/ibc/releases/download/${IBC_VERSION}/IBCLinux-${IBC_VERSION}.zip \ 48 | && unzip /tmp/IBC.zip -d ${IBC_PATH} \ 49 | && chmod -R u+x ${IBC_PATH}/*.sh \ 50 | && chmod -R u+x ${IBC_PATH}/scripts/*.sh \ 51 | && apt-get remove -y unzip \ 52 | && rm -f /tmp/IBC.zip 53 | 54 | # Install ib_insync 55 | RUN apt-get install -y git \ 56 | && pip install ib_insync \ 57 | #&& pip install git+https://github.com/canada4663/ib_insync \ 58 | && pip install psutil \ 59 | && apt-get remove -y git 60 | 61 | 62 | # Add xvfb/vnc/bin scripts 63 | ENV DISPLAY=:0 64 | COPY init /etc/init.d 65 | COPY root /root 66 | COPY jts.ini /opt/ibc/ 67 | RUN chmod u+x /etc/init.d/* \ 68 | && chmod u+x /root/* 69 | 70 | # expose ibg and vnc port 71 | EXPOSE 4003 72 | 73 | #ENTRYPOINT ["/usr/local/bin/python", "/root/ibcStart_docker.py"] 74 | CMD ["/root/start.sh"] 75 | -------------------------------------------------------------------------------- /ibc_docker/build.sh: -------------------------------------------------------------------------------- 1 | docker build -t ibc . 2 | -------------------------------------------------------------------------------- /ibc_docker/ecr_push.sh: -------------------------------------------------------------------------------- 1 | source build.sh 2 | $(aws ecr get-login --no-include-email --region us-east-2) 3 | docker tag ibc:latest 610953770981.dkr.ecr.us-east-2.amazonaws.com/ibc:latest 4 | docker push 610953770981.dkr.ecr.us-east-2.amazonaws.com/ibc:latest 5 | -------------------------------------------------------------------------------- /ibc_docker/init/xvfb: -------------------------------------------------------------------------------- 1 | XVFB=/usr/bin/Xvfb 2 | XVFBARGS="$DISPLAY -ac -screen 0 1024x768x16 +extension RANDR" 3 | PIDFILE=/var/xvfb_${DISPLAY}.pid 4 | case "$1" in 5 | start) 6 | echo -n "Starting virtual X frame buffer: Xvfb" 7 | /sbin/start-stop-daemon --start --quiet --pidfile $PIDFILE --make-pidfile --background --exec $XVFB -- $XVFBARGS 8 | echo "." 9 | ;; 10 | stop) 11 | echo -n "Stopping virtual X frame buffer: Xvfb" 12 | /sbin/start-stop-daemon --stop --quiet --pidfile $PIDFILE 13 | echo "." 14 | ;; 15 | restart) 16 | $0 stop 17 | $0 start 18 | ;; 19 | *) 20 | echo "Usage: /etc/init.d/xvfb {start|stop|restart}" 21 | exit 1 22 | esac 23 | exit 0 24 | -------------------------------------------------------------------------------- /ibc_docker/jts.ini: -------------------------------------------------------------------------------- 1 | [IBGateway] 2 | WriteDebug=false 3 | TrustedIPs=127.0.0.1,0.0.0.0 4 | MainWindow.Height=706 5 | LocalServerPort=4000 6 | ApiOnly=true 7 | MainWindow.Width=967 8 | 9 | [Logon] 10 | useRemoteSettings=false 11 | TimeZone=America/Chicago 12 | Individual=1 13 | tradingMode=l 14 | colorPalletName=dark 15 | Steps=7 16 | Locale=en 17 | SupportsSSL=gdc1.ibllc.com:4000,true,20190314,false;ndc1.ibllc.com:4000,true,20190601,false;ndc1_hb1.ibllc.com:4000,true,20190414,false 18 | UseSSL=true 19 | os_titlebar=false 20 | s3store=true 21 | 22 | [Communication] 23 | ctciAutoEncrypt=false 24 | Peer=ndc1.ibllc.com:4001 25 | Region=us 26 | 27 | -------------------------------------------------------------------------------- /ibc_docker/root/config_template.ini: -------------------------------------------------------------------------------- 1 | # Note that in the comments in this file, TWS refers to either the Trader 2 | # Workstation or the Gateway for the IB API. 3 | # 4 | # When referred to below, the default value for a setting is the value 5 | # assumed if either the setting is included but no value is specified, or 6 | # the setting is not included at all. 7 | # 8 | # IBC may also be used to start the FIX CTCI Gateway. All settings 9 | # relating to this have names prefixed with FIX. 10 | # 11 | # The IB API Gateway and the FIX CTCI Gateway share the same code. Which 12 | # gateway actually runs is governed by an option on the initial gateway 13 | # login screen. The FIX setting described under IBC Startup 14 | # Settings below controls this. 15 | 16 | 17 | # 1. IBC Startup Settings 18 | # ------------------------- 19 | 20 | 21 | # IBC may be used to start the IB Gateway for the FIX CTCI. This 22 | # setting must be set to 'yes' if you want to run the FIX CTCI gateway. The 23 | # default is 'no'. 24 | 25 | FIX=no 26 | 27 | 28 | # 2. Authentication Settings 29 | # ---------------------------- 30 | # 31 | # TWS and the IB API gateway require a single username and password. 32 | # You may specify the username and password using the following settings: 33 | # 34 | # IbLoginId 35 | # IbPassword 36 | # 37 | # Alternatively, you can specify the username and password in the command 38 | # files used to start TWS or the Gateway, but this is not recommended for 39 | # security reasons. 40 | # 41 | # If you don't specify them, you will be prompted for them in the usual 42 | # login dialog when TWS starts (but whatever you have specified will be 43 | # included in the dialog automatically: for example you may specify the 44 | # username but not the password, and then you will be prompted for the 45 | # password via the login dialog). Note that if you specify either 46 | # the username or the password (or both) in the command file, then 47 | # IbLoginId and IbPassword settings defined in this file are ignored. 48 | # 49 | # 50 | # The FIX CTCI gateway requires one username and password for FIX order 51 | # routing, and optionally a separate username and password for market 52 | # data connections. You may specify the usernames and passwords using 53 | # the following settings: 54 | # 55 | # FIXLoginId 56 | # FIXPassword 57 | # IbLoginId (optional - for market data connections) 58 | # IbPassword (optional - for market data connections) 59 | # 60 | # Alternatively you can specify the FIX username and password in the 61 | # command file used to start the FIX CTCI Gateway, but this is not 62 | # recommended for security reasons. 63 | # 64 | # If you don't specify them, you will be prompted for them in the usual 65 | # login dialog when FIX CTCI gateway starts (but whatever you have 66 | # specified will be included in the dialog automatically: for example 67 | # you may specify the usernames but not the passwords, and then you will 68 | # be prompted for the passwords via the login dialog). Note that if you 69 | # specify either the FIX username or the FIX password (or both) on the 70 | # command line, then FIXLoginId and FIXPassword settings defined in this 71 | # file are ignored; he same applies to the market data username and 72 | # password. 73 | 74 | # IB API Authentication Settings 75 | # ------------------------------ 76 | 77 | # Your TWS username: 78 | 79 | IbLoginId={ib_user} 80 | 81 | 82 | # Your TWS password: 83 | 84 | IbPassword={ib_password} 85 | 86 | 87 | # FIX CTCI Authentication Settings 88 | # -------------------------------- 89 | 90 | # Your FIX CTCI username: 91 | 92 | FIXLoginId= 93 | 94 | 95 | # Your FIX CTCI password: 96 | 97 | FIXPassword= 98 | 99 | 100 | # Trading Mode 101 | # ------------ 102 | # 103 | # TWS 955 introduced a new Trading Mode combo box on its login 104 | # dialog. This indicates whether the live account or the paper 105 | # trading account corresponding to the supplied credentials is 106 | # to be used. The allowed values are 'live' (the default) and 107 | # 'paper'. For earlier versions of TWS this setting has no 108 | # effect. 109 | 110 | #TradingMode=live 111 | 112 | 113 | # 3. TWS Startup Settings 114 | # ------------------------- 115 | # 116 | # Path to the directory where TWS should store its settings. This is 117 | # normally the folder in which TWS is installed. However you may set 118 | # it to some other location if you wish (for example if you want to 119 | # run multiple instances of TWS with different settings). 120 | # 121 | # It is recommended for clarity that you use an absolute path. The 122 | # effect of using a relative path is undefined. 123 | # 124 | # Linux and OS X users should use the appropriate path syntax. 125 | # 126 | # Note that, for Windows users, you MUST use double separator 127 | # characters to separate the elements of the folder path: for 128 | # example, IbDir=C:\\IBLiveSettings is valid, but 129 | # IbDir=C:\IBLiveSettings is NOT valid and will give unexpected 130 | # results. Linux and OS X users need not use double separators, 131 | # but they are acceptable. 132 | # 133 | # The default is the current working directory when IBC is 134 | # started. 135 | 136 | IbDir=/opt/ibc 137 | 138 | 139 | # If you wish to store your TWS settings on IB's servers rather 140 | # than locally on your computer, set this to 'yes' 141 | 142 | StoreSettingsOnServer=no 143 | 144 | 145 | # Set to 'yes' to minimise TWS when it starts: 146 | 147 | MinimizeMainWindow=no 148 | 149 | 150 | # When TWS logs on it checks to see whether the account is already 151 | # logged in at another computer. If so it displays a dialog: this 152 | # setting instructs TWS how to proceed. If set to 'primary', TWS 153 | # ends the existing session and continues with the new session. If 154 | # set to 'secondary', TWS exits so that the existing session is 155 | # unaffected. If set to 'manual', the user must handle the dialog. 156 | # Note that when set to 'primary', if another TWS session is 157 | # started and manually told to end the primary session, the primary 158 | # session is automatically reconnected (provided the other session 159 | # is in a TWS version higher than 949). Also note that if two 160 | # primary sessions are started, they will both keep reconnecting 161 | # and disconnecting the other: therefore care needs to be exercised 162 | # in the use of this setting. The default is 'manual'. 163 | 164 | ExistingSessionDetectedAction=manual 165 | 166 | 167 | # If set to 'accept', IBC automatically accepts incoming 168 | # API connection dialogs. If set to 'reject', IBC 169 | # automatically rejects incoming API connection dialogs. If 170 | # set to 'manual', the user must decide whether to accept or reject 171 | # incoming API connection dialogs. The default is 'manual'. 172 | # NB: it is recommended to set this to 'reject', and to explicitly 173 | # configure which IP addresses can connect to the API in TWS's API 174 | # configuration page, as this is much more secure (in this case, no 175 | # incoming API connection dialogs will occur for those IP addresses). 176 | 177 | AcceptIncomingConnectionAction=manual 178 | 179 | 180 | # NB: ShowAllTrades is deprecated starting with TWS 955. This is 181 | # because IB have changed the Trades window in such a way that it 182 | # is no longer possible to programmatically set the desired option. 183 | # However the user's setting is now persisted within and across 184 | # TWS sessions, so ShowAllTrades is no longer really needed. If 185 | # ShowAllTrades is set to 'yes', it will have no observable effect. 186 | # 187 | # The following description applies to TWS versions BEFORE TWS 955: 188 | # 189 | # If ShowAllTrades is set to 'yes', IBC causes TWS to 190 | # display the Trades log at startup, and sets the 'All' checkbox 191 | # to ensure that the API reports all executions that have occurred 192 | # during the past week. Moreover, any attempt by the user to change 193 | # any of the 'Show trades' checkboxes is ignored; similarly if the 194 | # user closes the Trades log, it is immediately re-displayed with 195 | # the 'All' checkbox set. If set to 'no', IBC does not 196 | # interact with the Trades log, and only the current session's 197 | # executions are returned via the API (unless the user changes the 198 | # Trades log checkboxes). The default is 'no'. 199 | 200 | ShowAllTrades=no 201 | 202 | 203 | # If OverrideTwsApiPort is set to an integer, IBC changes the 204 | # 'Socket port' in TWS's API configuration to that number shortly 205 | # after startup. Leaving the setting blank will make no change to 206 | # the current setting. This setting is only intended for use in 207 | # certain specialized situations where the port number needs to 208 | # be set dynamically at run-time: most users will never need it, 209 | # so don't use it unless you know you need it. 210 | 211 | OverrideTwsApiPort=4001 212 | 213 | 214 | # If ReadOnlyLogin is set to 'yes', and the user is enrolled in IB's 215 | # account security programme, the user will not be asked to supply 216 | # the security code, and login to TWS will occur automatically in 217 | # read-only mode: in this mode, placing or managing orders is not 218 | # allowed. If set to 'no', and the user is enrolled in IB's account 219 | # security programme, the user must supply the relevant security 220 | # code to complete the login. If the user is not enrolled in IB's 221 | # account security programme, this setting is ignored. The default 222 | # is 'no'. 223 | 224 | ReadOnlyLogin=yes 225 | 226 | 227 | # Logging in to a paper-trading account results in TWS displaying 228 | # a dialog asking the user to confirm that they are aware that this 229 | # is not a brokerage account. Until this dialog has been accepted, 230 | # TWS will not allow API connections to succeed. Setting this 231 | # to 'yes' (the default) will cause IBC to automatically 232 | # confirm acceptance. Setting it to 'no' will leave the dialog 233 | # on display, and the user will have to deal with it manually. 234 | 235 | AcceptNonBrokerageAccountWarning=yes 236 | 237 | 238 | # 4. TWS Auto-Closedown 239 | # ----------------------- 240 | # 241 | # Set to yes or no (lower case). Only affects TWS, not the Gateway. 242 | # 243 | # yes means allow TWS to shut down automatically at its 244 | # specified shutdown time, which is set via the TWS 245 | # configuration menu. 246 | # 247 | # no means TWS never shuts down automatically. 248 | # 249 | # NB: IB recommends that you do not keep TWS running 250 | # continuously. If you set this setting to 'no', you may 251 | # experience incorrect TWS operation. 252 | # 253 | # Starting with TWS 974, this setting no longer works properly, 254 | # because IB have changed the way TWS handles its autologoff 255 | # mechanism. You should now configure the TWS autologoff time 256 | # to something convenient for you, and restart IBC each day. 257 | # Alternatively, discontinue use of IBC and use the autorestart 258 | # mechanism within TWS 974 and later versions. 259 | 260 | IbAutoClosedown=yes 261 | 262 | 263 | 264 | # 5. TWS Tidy Closedown Time 265 | # ---------------------------- 266 | # 267 | # NB: starting with TWS 974 this is no longer a useful option 268 | # for TWS, because of changes in TWS 974's autologoff handling. 269 | # However it can still be useful with the Gateway. 270 | # 271 | # To tell IBC to tidily close TWS at a specified time every 272 | # day, set this value to , for example: 273 | # ClosedownAt=22:00 274 | # 275 | # To tell IBC to tidily close TWS at a specified day and time 276 | # each week, set this value to , for example: 277 | # ClosedownAt=Friday 22:00 278 | # 279 | # Note that the day of the week must be specified using your 280 | # default locale. Also note that Java will only accept 281 | # characters encoded to ISO 8859-1 (Latin-1). This means that 282 | # if the day name in your default locale uses any non-Latin-1 283 | # characters you need to encode them using Unicode escapes 284 | # (see http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.3 285 | # for details). For example, to tidily close TWS at 12:00 on 286 | # Saturday where the default locale is Simplified Chinese, 287 | # use the following: 288 | # #ClosedownAt=\u661F\u671F\u516D 12:00 289 | 290 | ClosedownAt= 291 | 292 | 293 | 294 | # 6. Other TWS Settings 295 | # ----------------------- 296 | # 297 | # If you attempt to place an order for a contract for which 298 | # you have no market data subscription, TWS displays a dialog 299 | # to warn you against such blind trading. 300 | # 301 | # yes means the dialog is dismissed as though the user had 302 | # clicked the 'Ok' button: this means that you accept 303 | # the risk and want the order to be submitted. 304 | # 305 | # no means the dialog remains on display and must be 306 | # handled by the user. 307 | 308 | AllowBlindTrading=no 309 | 310 | 311 | # Indian versions of TWS may display a password expiry 312 | # notification dialog and a NSE Compliance dialog. These can be 313 | # dismissed by setting the following to yes. By default the 314 | # password expiry notice is not dismissed, but the NSE Compliance 315 | # notice is dismissed. 316 | 317 | # Warning: setting DismissPasswordExpiryWarning=yes will mean 318 | # you will not be notified when your password is about to expire. 319 | # You must then take other measures to ensure that your password 320 | # is changed within the expiry period, otherwise IBC will 321 | # not be able to login successfully. 322 | 323 | DismissPasswordExpiryWarning=no 324 | DismissNSEComplianceNotice=yes 325 | 326 | 327 | # Since TWS 906, orders placed using the BookTrader have to be confirmed 328 | # via a dialog box (this was due to a legal dispute between IB and Trading 329 | # Technologies who claimed IB were infringing a patent). If 330 | # AutoConfirmOrders=yes, then when orders are placed using the BookTrader, 331 | # the confirmation dialog is automatically handled, thereby effectively 332 | # restoring the one-click trading. The default is 'no', requiring the user 333 | # to manually confirm each trade. 334 | # 335 | # NB: this setting has been removed as the dispute has been resolved and 336 | # TWS users now have the option to request the order confirmation dialog 337 | # to not be displayed. 338 | # 339 | #AutoConfirmOrders=no 340 | 341 | 342 | # You can tell TWS to automatically save its settings on a schedule 343 | # of your choosing. You can specify one or more specific times, 344 | # like this: 345 | # 346 | # SaveTwsSettingsAt=HH:MM [ HH:MM]... 347 | # 348 | # for example: 349 | # SaveTwsSettingsAt=08:00 12:30 17:30 350 | # 351 | # Or you can specify an interval at which settings are to be saved, 352 | # optionally starting at a specific time and continuing until another 353 | # time, like this: 354 | # 355 | #SaveTwsSettingsAt=Every n [{mins | hours}] [hh:mm] [hh:mm] 356 | # 357 | # where the first hh:mm is the start time and the second is the end 358 | # time. If you don't specify the end time, settings are saved regularly 359 | # from the start time till midnight. If you don't specify the start time. 360 | # settings are saved regularly all day, beginning at 00:00. Note that 361 | # settings will always be saved at the end time, even if that is not 362 | # exactly one interval later than the previous time. If neither 'mins' 363 | # nor 'hours' is specified, 'mins' is assumed. Examples: 364 | # 365 | # To save every 30 minutes all day starting at 00:00 366 | #SaveTwsSettingsAt=Every 30 367 | #SaveTwsSettingsAt=Every 30 mins 368 | # 369 | # To save every hour starting at 08:00 and ending at midnight 370 | #SaveTwsSettingsAt=Every 1 hours 08:00 371 | #SaveTwsSettingsAt=Every 1 hours 08:00 00:00 372 | # 373 | # To save every 90 minutes starting at 08:00 up to and including 17:43 374 | #SaveTwsSettingsAt=Every 90 08:00 17:43 375 | 376 | SaveTwsSettingsAt= 377 | 378 | 379 | 380 | # 6. IBC Command Server Settings 381 | # -------------------------------- 382 | # 383 | # Do NOT CHANGE THE FOLLOWING SETTINGS unless you 384 | # intend to issue commands to IBC (for example 385 | # using telnet). Note that these settings have nothing to 386 | # do with running programs that use the TWS API. 387 | 388 | # The port that IBC listens on for commands 389 | # such as "STOP". DO NOT set this to the port number 390 | # used for TWS API connections. There is no good reason 391 | # to change this setting unless the port is used by 392 | # some other application (typically another instance of 393 | # IBC). The default value is 0, which tells IBC not to 394 | # start the command server 395 | 396 | #CommandServerPort=7462 397 | 398 | 399 | # A comma separated list of ip addresses, or host names, 400 | # which are allowed addresses for sending commands to 401 | # IBC. Commands can always be sent from the 402 | # same host as IBC is running on. 403 | 404 | ControlFrom= 405 | 406 | 407 | # Specifies the IP address on which the Command Server 408 | # is to listen. For a multi-homed host, this can be used 409 | # to specify that connection requests are only to be 410 | # accepted on the specified address. The default is to 411 | # accept connection requests on all local addresses. 412 | 413 | BindAddress= 414 | 415 | 416 | # The specified string is output by the server when 417 | # the connection is first opened and after the completion 418 | # of each command. This can be useful if sending commands 419 | # using an interactive program such as telnet. The default 420 | # is that no prompt is output. 421 | # For example: 422 | # 423 | # CommandPrompt=> 424 | 425 | CommandPrompt= 426 | 427 | 428 | # Some commands can return intermediate information about 429 | # their progress. This setting controls whether such 430 | # information is sent. The default is that such information 431 | # is not sent. 432 | 433 | SuppressInfoMessages=yes 434 | 435 | 436 | 437 | # 7. Diagnostic Settings 438 | # ------------------------ 439 | 440 | # If LogComponents is set to 'open' or 'yes' or 'true', 441 | # IBC logs information about the structure of each 442 | # TWS window it detects the first time it is encountered. If 443 | # set to 'activate', the information is logged every time 444 | # a TWS window is made active. If set to 'never' or 'no' or 445 | # 'false', this information is never logged. The default is 446 | # 'never'. 447 | # 448 | # The logged information shows the hierarchical organisation 449 | # of all the components of the window, and includes the 450 | # current values of text boxes and labels. 451 | # 452 | # Note that when set to 'open', 'activate' or 'yes', there is 453 | # a small performance impact due to the extra logging. Also 454 | # logfile size may be significantly increased, especially if 455 | # set to 'activate' and the user is actively using TWS. It 456 | # is therefore recommended that the setting be left at 'no' 457 | # unless there is a specific reason that this information is 458 | # needed. 459 | 460 | LogComponents=never 461 | 462 | -------------------------------------------------------------------------------- /ibc_docker/root/ibcStart_docker.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | import json 4 | import base64 5 | import boto3 6 | from ib_insync import Stock, Future, util 7 | from ib_insync import ibcontroller 8 | from ib_insync import IB 9 | from ib_insync.ibcontroller import Watchdog 10 | import ib_insync.util as util 11 | from updateSecrets import updateSecrets 12 | 13 | util.logToConsole(logging.DEBUG) 14 | logger = logging.getLogger(name=__name__) 15 | ibcPath = os.getenv("IBC_PATH", "/opt/ibc") 16 | homePath = os.getenv("HOME", "/root") 17 | twsPath = os.getenv("TWS_PATH", "~/Jts") 18 | twsLiveorPaperMode = os.getenv("TWS_LIVE_PAPER", "paper") 19 | firehoseStream = os.getenv("FIREHOSE_STREAM_NAME", "ibc-paper") 20 | secretsPath = os.getenv("SECRETS_PATH", "/ibc/paper/") 21 | configTemplate = homePath + "/config_template.ini" 22 | config = homePath + "/config.ini" 23 | # TODO: get region from environment variables 24 | firehose = boto3.client("firehose", region_name="us-east-2") 25 | 26 | logger.info("Updating Secrets") 27 | updateSecrets(configTemplate, config, secretsPath=secretsPath) 28 | 29 | ibc = ibcontroller.IBC( 30 | twsVersion=972, 31 | gateway=True, 32 | ibcPath=ibcPath, 33 | tradingMode=twsLiveorPaperMode, 34 | twsSettingsPath=twsPath, 35 | ibcIni=config, 36 | ) 37 | 38 | ibc.start() 39 | ib = IB() 40 | watchdog = Watchdog( 41 | ibc, 42 | ib, 43 | port=4001, 44 | connectTimeout=59, 45 | appStartupTime=45, 46 | appTimeout=59, 47 | retryDelay=10, 48 | ) 49 | watchdog.start() 50 | 51 | ib.sleep(60) 52 | 53 | #TODO: Add this 'setup' to a different event handler to fire when connected 54 | STOCK = [ 55 | "SPY", 56 | "QQQ", 57 | "IWM", 58 | "VXX", 59 | "GLD", 60 | "AMZN", 61 | "GOOG", 62 | "EFA", 63 | "EEM", 64 | "TLT", 65 | "USO", 66 | "GDX", 67 | "GDXJ", 68 | "XLF", 69 | "XLE", 70 | "XLU", 71 | "XRT", 72 | "XLK", 73 | "XME", 74 | "FXI", 75 | "EWZ", 76 | "FB", 77 | "AAPL", 78 | "NFLX", 79 | "MSFT", 80 | "BABA", 81 | "INTC", 82 | "TSLA", 83 | ] 84 | FUTURES = ["ES", "NQ", "RTY", "CL", "NG", "ZB", "ZN", "GC", "MXP", "EUR", "JPY", "GBP"] 85 | 86 | stockContracts = [Stock(s, "SMART", "USD") for s in STOCK] 87 | ib.qualifyContracts(*stockContracts) 88 | 89 | futures = [ib.reqContractDetails(Future(f)) for f in FUTURES] 90 | futuresContracts = [c.contract for f in futures for c in f] 91 | futuresContracts = [ 92 | c 93 | for c in futuresContracts 94 | if c.tradingClass == c.symbol and c.lastTradeDateOrContractMonth.startswith("2019") 95 | ] 96 | 97 | for contract in stockContracts + futuresContracts: 98 | ib.reqMktData(contract, "", False, False) 99 | 100 | def onPendingTickers(tickers): 101 | ticks = [] 102 | for t in tickers: 103 | encodedTick = json.dumps(util.tree(t)) 104 | ticks.append({"Data": encodedTick}) 105 | firehose.put_record_batch(DeliveryStreamName=firehoseStream, Records=ticks) 106 | 107 | 108 | ib.pendingTickersEvent += onPendingTickers 109 | 110 | IB.run() 111 | -------------------------------------------------------------------------------- /ibc_docker/root/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | service xvfb start 4 | /usr/bin/socat TCP-LISTEN:4003,fork TCP:127.0.0.1:4001 & 5 | /usr/local/bin/python /root/ibcStart_docker.py 6 | -------------------------------------------------------------------------------- /ibc_docker/root/updateSecrets.py: -------------------------------------------------------------------------------- 1 | import os 2 | import fileinput 3 | from shutil import copyfile 4 | import boto3 5 | import ssm_parameter_store as ssm 6 | 7 | CONFIG_FILE_TEMPLATE = 'config_template.ini' 8 | CONFIG_FILE = 'config.ini' 9 | 10 | def updateSecrets(templateFile=CONFIG_FILE_TEMPLATE, 11 | outputFile=CONFIG_FILE, 12 | secretsPath='/ibc/paper/'): 13 | 14 | store = ssm.EC2ParameterStore() 15 | secrets = store.get_parameters_with_hierarchy(secretsPath) 16 | 17 | if secrets.get('TWS_USER', False): 18 | userName = secrets['TWS_USER'] 19 | elif os.environ.get('TWS_USER', False): 20 | userName = os.environ['TWS_USER'] 21 | else: 22 | raise Exception("ERROR: No IB Username Set") 23 | 24 | if secrets.get('TWS_PASSWORD', False): 25 | userPassword = secrets['TWS_PASSWORD'] 26 | elif os.environ.get('TWS_PASSWORD', False): 27 | userPassword = os.environ['TWS_PASSWORD'] 28 | else: 29 | raise Exception("ERROR: No IB Password Set") 30 | 31 | copyfile(templateFile, outputFile) 32 | 33 | with fileinput.FileInput(outputFile, inplace=True) as file: 34 | for line in file: 35 | line = line.replace('{ib_user}', userName) 36 | line = line.replace('{ib_password}', userPassword) 37 | print(line, end='') 38 | 39 | return outputFile 40 | -------------------------------------------------------------------------------- /ibc_docker/run.sh: -------------------------------------------------------------------------------- 1 | docker rm ibg 2 | docker run -d -e IB_USER=${IB_USER} -e IB_PASSWORD=${IB_PASSWORD} -e TWS_LIVE_PAPER=${TWS_LIVE_PAPER} \ 3 | --publish 127.0.0.1:4003:4003 --name ibg ibc 4 | -------------------------------------------------------------------------------- /ibc_docker/run_interactive.sh: -------------------------------------------------------------------------------- 1 | docker rm ibg 2 | docker run -e IB_USER=${IB_USER} -e IB_PASSWORD=${IB_PASSWORD} -e TWS_LIVE_PAPER=${TWS_LIVE_PAPER} \ 3 | -it --publish 127.0.0.1:4003:4003 --name ibg ibc /bin/bash 4 | --------------------------------------------------------------------------------