├── .gitignore
├── dockerfile_mt5
├── root
│ └── defaults
│ │ ├── autostart
│ │ └── menu.xml
├── Dockerfile
└── Metatrader
│ └── start.sh
├── dockerfile_reverseproxy
├── Dockerfile
└── nginx.conf
├── dockerfile_superset
└── Dockerfile
├── .env
├── dockerfile_jupyter_notebook
├── requirements.txt
└── Dockerfile
├── dockerfile_airflow
├── requirements.txt
├── Dockerfile
└── plot.py
├── starter_script.sh
├── ibgateway
├── components
│ ├── services
│ │ ├── socat
│ │ │ └── run
│ │ ├── ibc
│ │ │ └── run
│ │ ├── xvfb
│ │ │ └── run
│ │ └── vnc
│ │ │ └── run
│ ├── ibgateway
│ │ └── jts.ini
│ └── ibc
│ │ ├── ibc.ini
│ │ └── config.ini
└── Dockerfile
├── README.md
├── docker-compose.yaml
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | .env
3 | .env
4 |
--------------------------------------------------------------------------------
/dockerfile_mt5/root/defaults/autostart:
--------------------------------------------------------------------------------
1 | /Metatrader/start.sh
--------------------------------------------------------------------------------
/dockerfile_reverseproxy/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM nginx
2 |
3 | COPY nginx.conf /etc/nginx/nginx.conf
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/dockerfile_superset/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM apache/superset:4.1.1-py311
2 | USER root
3 | RUN apt-get update && apt-get install -y \
4 | bash vim \
5 | g++ make wget git curl gcc gnupg2
6 | #USER superset
7 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | WD=/home/mariano/docker
2 | ND=/home/mariano/docker/notebooks
3 | TWSUSERID=user
4 | TWSPASSWORD=password
5 | TRADING_MODE=paper
6 | TZ=America/Bogota
7 | AIRFLOW_UID=1000
8 | AIRFLOW_GID=0
9 | CUSTOM_USER=username
10 | PASSWORD=
11 | MAPBOX_API_KEY=
12 |
--------------------------------------------------------------------------------
/dockerfile_jupyter_notebook/requirements.txt:
--------------------------------------------------------------------------------
1 | numpy
2 | pandas
3 | psycopg2-binary
4 | scikit-learn
5 | backtrader2
6 | ib_insync
7 | bs4
8 | peewee
9 | adjustText
10 | xlrd
11 | ffn
12 | pyfolio
13 | quantstats
14 | riskfolio_lib==4.4.2
15 | black_box
16 | statsmodels
17 | yfinance
18 | scipy
19 | requests
20 | pandas_market_calendars
21 | pandas_datareader
22 | graphviz
23 | pandas_profiling
24 | tqdm
25 | ib_insync
26 | pytz
27 | tabulate
28 | joblib
29 | ccxt
30 | ibapi
31 | PyYAML
32 | xverse
33 | chart-studio
34 |
--------------------------------------------------------------------------------
/dockerfile_mt5/root/defaults/menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
--------------------------------------------------------------------------------
/dockerfile_airflow/requirements.txt:
--------------------------------------------------------------------------------
1 | pandas==2.2.3
2 | numpy==2.1.3
3 | matplotlib
4 | plotly==5.24.1
5 | seaborn
6 | riskfolio-lib==4.4.2
7 | cvxpy[ecos]
8 | vectorbt
9 | backtrader2
10 | pandas_market_calendars
11 | pandas_datareader
12 | yfinance
13 | metastock2pd
14 | TA_Lib
15 | humanize
16 | websocket
17 | tables
18 | kaleido
19 | git+https://github.com/ultra1971/mt5linux.git
20 | psycopg2-binary
21 | scikit-learn
22 | lightgbm
23 | xgboost
24 | ib_insync
25 | bs4
26 | peewee
27 | adjustText
28 | xlrd
29 | ffn
30 | quantstats
31 | black_box
32 | statsmodels
33 | shap
34 | scipy
35 | graphviz
36 | pandas_profiling
37 | tqdm
38 | pytz
39 | tabulate
40 | joblib
41 | pyyamL
42 | lxml
43 | cssselect
44 | xverse
45 | pykalman
46 | dataframe_image
47 | chart-studio
48 | metastock2pd
49 | mplfinance
50 | rich
51 |
--------------------------------------------------------------------------------
/starter_script.sh:
--------------------------------------------------------------------------------
1 | echo "create folders needed"
2 | mkdir ./airflow
3 | mkdir ./airflow/dags
4 | mkdir ./airflow/logs
5 | mkdir ./airflow/plugins
6 | mkdir ./mlflow
7 | mkdir ./notebooks
8 | mkdir ./pgadmin
9 | mkdir ./postgress_db
10 | mkdir ./superset
11 |
12 | echo "build algotrading image"
13 | docker-compose build
14 |
15 | echo "run algotrading image"
16 | docker-compose up -d
17 |
18 | echo "scripts on superset"
19 | docker exec -it superset superset fab create-admin \
20 | --username admin \
21 | --firstname Superset \
22 | --lastname Admin \
23 | --email admin@superset.com \
24 | --password admin
25 | docker exec -it superset superset db upgrade
26 | docker exec -it superset superset init
27 | docker exec -it superset superset load_examples
28 |
29 |
--------------------------------------------------------------------------------
/ibgateway/components/services/socat/run:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | shopt -s expand_aliases
4 |
5 | declare -r service_name='socat'
6 |
7 | source /etc/profile
8 |
9 | alias debug="while read data; do echo -e "${data}" | log debug; done"
10 | alias info="while read data; do echo -e "${data}" | log info; done"
11 | alias warn="while read data; do echo -e "${data}" | log warning; done"
12 | alias err="while read data; do echo -e "${data}" | log err; done"
13 |
14 | function log(){
15 | # ATENCIÓN: NUNCA USAR DIRECTAMENTE, en su lugar utilizar los alias 'debug', 'info', 'warn' y 'err'
16 | local _severity=${1}; shift;
17 | logger -p syslog.${_severity} -t ${service_name} -- "${data}"
18 | }
19 |
20 | echo "Starting Interactive Brokers Controller" | info
21 | exec socat TCP-LISTEN:4001,fork TCP:127.0.0.1:4002 2>&1 | info
22 |
--------------------------------------------------------------------------------
/ibgateway/components/services/ibc/run:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | shopt -s expand_aliases
4 |
5 | declare -r service_name='ibcontroller'
6 |
7 | source /etc/profile
8 |
9 | alias debug="while read data; do echo -e "${data}" | log debug; done"
10 | alias info="while read data; do echo -e "${data}" | log info; done"
11 | alias warn="while read data; do echo -e "${data}" | log warning; done"
12 | alias err="while read data; do echo -e "${data}" | log err; done"
13 |
14 | function log(){
15 | # ATENCIÓN: NUNCA USAR DIRECTAMENTE, en su lugar utilizar los alias 'debug', 'info', 'warn' y 'err'
16 | local _severity=${1}; shift;
17 | logger -p syslog.${_severity} -t ${service_name} -- "${data}"
18 | }
19 |
20 | echo "Starting Interactive Brokers Controller" | info
21 | exec /opt/IBC/scripts/displaybannerandlaunch.sh 2>&1 | info
22 |
--------------------------------------------------------------------------------
/ibgateway/components/ibgateway/jts.ini:
--------------------------------------------------------------------------------
1 | [IBGateway]
2 | WriteDebug=false
3 | TrustedIPs=127.0.0.1
4 | MainWindow.Height=550
5 | RemoteHostOrderRouting=hdc1.ibllc.com
6 | RemotePortOrderRouting=4000
7 | LocalServerPort=4000
8 | ApiOnly=true
9 | MainWindow.Width=700
10 |
11 | [Logon]
12 | useRemoteSettings=false
13 | UserNameToDirectory=eedkjkhbocomaodbogcfjlfpfmgonliddbpihlmh,godbekfnldoaidmjmlfijhfenlbgmpmnjodpbjpj
14 | TimeZone=America/Argentina/Buenos_Aires
15 | tradingMode=p
16 | colorPalletName=dark
17 | Steps=7
18 | Locale=en
19 | SupportsSSL=gdc1.ibllc.com:4000,true,20200709,false;cdc1.ibllc.com:4000,true,20200716,false
20 | UseSSL=true
21 | os_titlebar=false
22 | s3store=true
23 | ibkrBranding=pro
24 |
25 | [ns]
26 | darykq=1
27 |
28 | [Communication]
29 | SettingsDir=/root/Jts
30 | Peer=cdc1.ibllc.com:4001
31 | Region=hk
32 |
33 |
--------------------------------------------------------------------------------
/ibgateway/components/services/xvfb/run:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | shopt -s expand_aliases
4 |
5 | declare -r service_name='xvfb'
6 |
7 | source /etc/profile
8 |
9 | alias debug="while read data; do echo -e "${data}" | log debug; done"
10 | alias info="while read data; do echo -e "${data}" | log info; done"
11 | alias warn="while read data; do echo -e "${data}" | log warning; done"
12 | alias err="while read data; do echo -e "${data}" | log err; done"
13 |
14 | function log(){
15 | # ATENCIÓN: NUNCA USAR DIRECTAMENTE, en su lugar utilizar los alias 'debug', 'info', 'warn' y 'err'
16 | local _severity=${1}; shift;
17 | logger -p syslog.${_severity} -t ${service_name} -- "${data}"
18 | }
19 |
20 | echo "Starting Xvfb - Virtual Framebuffer 'fake' X server" | info
21 | echo -e "$(env)" | debug
22 | exec /usr/bin/Xvfb $DISPLAY -ac -screen 0 1980x1020x24 +extension RANDR 2>&1 | info
23 |
--------------------------------------------------------------------------------
/dockerfile_airflow/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM apache/airflow:2.10.5-python3.11
2 |
3 | USER root
4 | RUN apt-get update && apt-get install -y \
5 | python3 unzip \
6 | python3-dev \
7 | libhdf5-serial-dev python3-tables python-tables-doc \
8 | bash vim font-manager xfonts-utils fontconfig\
9 | g++ make wget git curl gcc gnupg2 \
10 | && rm -rf /var/lib/apt/lists/*
11 |
12 | RUN curl -L https://github.com/ta-lib/ta-lib/releases/download/v0.6.4/ta-lib_0.6.4_amd64.deb -o ta-lib_0.6.4_amd64.deb
13 | RUN dpkg -i ta-lib_0.6.4_amd64.deb
14 |
15 | #RUN pip3 install numpy \
16 | # && pip3 install TA-Lib
17 |
18 | ENV NUMBA_CACHE_DIR=/tmp
19 |
20 | USER airflow
21 |
22 | COPY requirements.txt /tmp/
23 | RUN pip3 install --requirement /tmp/requirements.txt
24 |
25 | COPY plot.py /home/airflow/.local/lib/python3.9/site-packages/backtrader/plot/plot.py
26 |
27 | ENV PYTHONPATH "${PYTHONPATH}:/usr/local/airflow/dags"
28 | ENV NUMBA_CACHE_DIR=/tmp
29 |
--------------------------------------------------------------------------------
/ibgateway/components/services/vnc/run:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | shopt -s expand_aliases
4 |
5 | declare -r service_name='vnc'
6 |
7 | source /etc/profile
8 |
9 | alias debug="while read data; do echo -e "${data}" | log debug; done"
10 | alias info="while read data; do echo -e "${data}" | log info; done"
11 | alias warn="while read data; do echo -e "${data}" | log warning; done"
12 | alias err="while read data; do echo -e "${data}" | log err; done"
13 |
14 | function log(){
15 | # ATENCIÓN: NUNCA USAR DIRECTAMENTE, en su lugar utilizar los alias 'debug', 'info', 'warn' y 'err'
16 | local _severity=${1}; shift;
17 | logger -p syslog.${_severity} -t ${service_name} -- "${data}"
18 | }
19 |
20 | echo "Starting VNC server to allow remote access to an existing X session" | info
21 | mkdir /.vnc && x11vnc -storepasswd "${VNC_PASSWORD}" /.vnc/passwd 2>&1 | info
22 | exec /usr/bin/x11vnc -forever -geometry 1980x1020 -rfbport ${VNC_PORT} -rfbauth /.vnc/passwd -display ${DISPLAY} 2>&1 | info
23 |
--------------------------------------------------------------------------------
/dockerfile_mt5/Dockerfile:
--------------------------------------------------------------------------------
1 | #FROM ghcr.io/linuxserver/baseimage-kasmvnc:amd64-debianbullseye-8446af38-ls104
2 | FROM ghcr.io/linuxserver/baseimage-kasmvnc:amd64-debianbookworm
3 |
4 | # set version label
5 | ARG BUILD_DATE
6 | ARG VERSION
7 | LABEL build_version="Metatrader Docker:- ${VERSION} Build-date:- ${BUILD_DATE}"
8 | LABEL maintainer="gmartin"
9 |
10 | ENV TITLE=Metatrader5
11 | ENV WINEPREFIX="/config/.wine"
12 |
13 | # Update package lists and upgrade packages
14 | RUN apt-get update && apt-get upgrade -y
15 |
16 | # Install required packages
17 | RUN apt-get install -y \
18 | python3-pip \
19 | wget vim curl git pipx
20 |
21 | #RUN pip3 install --upgrade pip
22 |
23 | # Add WineHQ repository key and APT source
24 | # && add-apt-repository 'deb https://dl.winehq.org/wine-builds/debian/ bullseye main' \
25 | RUN wget -q https://dl.winehq.org/wine-builds/winehq.key \
26 | && apt-key add winehq.key \
27 | && add-apt-repository 'deb https://dl.winehq.org/wine-builds/debian/ bookworm main' \
28 | && rm winehq.key
29 |
30 | # Add i386 architecture and update package lists
31 | RUN dpkg --add-architecture i386 \
32 | && apt-get update
33 |
34 | # Install WineHQ stable package and dependencies
35 | RUN apt-get install --install-recommends -y \
36 | winehq-stable winetricks \
37 | && apt-get clean \
38 | && rm -rf /var/lib/apt/lists/*
39 |
40 | COPY /Metatrader /Metatrader
41 | RUN chmod +x /Metatrader/start.sh
42 | COPY /root /
43 |
44 | #EXPOSE 3000 8001
45 | #VOLUME /config
46 |
--------------------------------------------------------------------------------
/dockerfile_reverseproxy/nginx.conf:
--------------------------------------------------------------------------------
1 | events { }
2 |
3 | http {
4 | server {
5 | listen 80;
6 | server_name algotrading;
7 |
8 | location /airflow/ {
9 | proxy_pass http://172.19.0.12:8080;
10 | }
11 |
12 | location /superset/ {
13 | proxy_set_header X-Real-IP $remote_addr;
14 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
15 | proxy_set_header X-NginX-Proxy true;
16 | proxy_pass http://172.19.0.23:8088;
17 | proxy_ssl_session_reuse off;
18 | proxy_set_header Host $http_host;
19 | proxy_cache_bypass $http_upgrade;
20 | proxy_redirect off;
21 |
22 | }
23 |
24 | location /mlflow {
25 | rewrite ^/projecta(.*) /$1 break;
26 | proxy_pass http://127.0.0.1:5500;
27 | }
28 | location /jupyter {
29 | rewrite ^/projecta(.*) /$1 break;
30 | proxy_pass http://127.0.0.1:8888;
31 | }
32 | location /trading {
33 | alias /etc/nginx/html/CEREBRO/OUTPUT/;
34 | index RECOMENDATIONS.html;
35 | }
36 | location /health {
37 | access_log off;
38 | return 200 "healthy\n";
39 | }
40 | }
41 |
42 | }
43 |
44 |
45 |
--------------------------------------------------------------------------------
/ibgateway/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM phusion/baseimage:noble-1.0.1
2 |
3 | USER root
4 | SHELL ["/bin/bash", "-c"]
5 |
6 | ENV DEBIAN_FRONTEND=noninteractive \
7 | DISABLE_SYSLOG=0 \
8 | DISABLE_SSH=1 \
9 | DISABLE_CRON=1 \
10 | DISPLAY=:0 \
11 | TZ=America/Bogota
12 |
13 | CMD ["/sbin/my_init"]
14 |
15 | RUN install_clean \
16 | wget \
17 | unzip \
18 | socat \
19 | xvfb \
20 | libxrender1 \
21 | libxtst6 \
22 | libxi6 \
23 | x11vnc \
24 | && echo "source /etc/profile" >> /root/.bashrc
25 |
26 | ENV VNC_PASSWORD=123456 \
27 | VNC_PORT=5900
28 | COPY components/services/xvfb /etc/service/00_xvfb
29 | COPY components/services/vnc /etc/service/01_vnc
30 | COPY components/services/ibc /etc/service/02_ibc
31 | COPY components/services/socat /etc/service/03_socat
32 |
33 | RUN chmod a+x /etc/service/*/*
34 |
35 | ###### Instalacion del IBGateway #######
36 | ENV IBGATEWAY_PKG_URL="https://download2.interactivebrokers.com/installers/ibgateway/stable-standalone/ibgateway-stable-standalone-linux-x64.sh"
37 | ADD ${IBGATEWAY_PKG_URL} /tmp/ibgateway.sh
38 | RUN chmod a+x /tmp/ibgateway.sh \
39 | && echo -e "\nn" | /tmp/ibgateway.sh -c \
40 | && rm -f /tmp/ibgateway.sh \
41 | && stat /usr/local/i4j_jres/**/**/bin | grep File | awk '{print "export JAVA_PATH="$2}' > /etc/profile.d/java.sh
42 | COPY components/ibgateway/* /root/Jts/
43 |
44 | ##### Instalacion del IBController #####
45 | ENV IBC_PKG_URL="https://github.com/IbcAlpha/IBC/releases/download/3.21.2/IBCLinux-3.21.2.zip" \
46 | IBC_INI=/root/IBC/config.ini \
47 | IBC_PATH=/opt/IBC \
48 | TWS_MAJOR_VRSN=1030 \
49 | TWS_PATH=/root/Jts \
50 | TWS_CONFIG_PATH=/root/Jts \
51 | LOG_PATH=/root/IBC/Logs \
52 | TRADING_MODE=paper \
53 | FIXUSERID='' \
54 | FIXPASSWORD='' \
55 | TWSUSERID="" \
56 | TWSPASSWORD="" \
57 | APP=GATEWAY
58 |
59 | ADD ${IBC_PKG_URL} /tmp/ibc.zip
60 | RUN mkdir -p /{root,opt}/IBC/Logs \
61 | && unzip /tmp/ibc.zip -d /opt/IBC/ \
62 | && cd /opt/IBC/ \
63 | && chmod o+x *.sh */*.sh \
64 | && sed -i 's/ >> \"\${log_file}\" 2>\&1/ 2>\&1/g' scripts/displaybannerandlaunch.sh \
65 | && sed -i 's/light_red=.*/light_red=""/g' scripts/displaybannerandlaunch.sh \
66 | && sed -i 's/light_green=.*/light_green=""/g' scripts/displaybannerandlaunch.sh \
67 | && rm -f /tmp/ibc.zip
68 | COPY components/ibc/* /root/IBC/
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/dockerfile_jupyter_notebook/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM jupyter/datascience-notebook:x86_64-python-3.11.6
2 |
3 | USER root
4 |
5 | RUN apt-get update && apt-get install -y \
6 | python3 \
7 | python3-dev \
8 | bash vim \
9 | g++ make wget git curl gcc gnupg2 \
10 | && rm -rf /var/lib/apt/lists/
11 |
12 | #RUN pip install numpy
13 | # TA-Lib
14 | #RUN wget http://prdownloads.sourceforge.net/ta-lib/ta-lib-0.4.0-src.tar.gz && \
15 | # tar -xvzf ta-lib-0.4.0-src.tar.gz && \
16 | # cd ta-lib/ && \
17 | # ./configure --prefix=/usr && \
18 | # make && \
19 | # make install
20 | #RUN rm -R ta-lib ta-lib-0.4.0-src.tar.gz
21 |
22 | RUN curl -L https://github.com/ta-lib/ta-lib/releases/download/v0.6.4/ta-lib_0.6.4_amd64.deb
23 | RUN dpkg -i ta-lib_0.6.4_amd64.deb
24 |
25 | RUN pip3 install numpy \
26 | && pip3 install TA-Lib
27 |
28 | COPY requirements.txt /tmp/
29 | RUN pip install --requirement /tmp/requirements.txt && \
30 | fix-permissions $CONDA_DIR && \
31 | fix-permissions /home/$NB_USER && \
32 | apt update && \
33 | apt-get install curl -y
34 |
35 | ENV PYTHONPATH "${PYTHONPATH}:/home/jovyan/work"
36 |
37 | RUN python -m pip install --upgrade --no-deps --force-reinstall notebook
38 |
39 | RUN python -m pip install jupyterthemes
40 | RUN python -m pip install --upgrade jupyterthemes
41 | RUN python -m pip install jupyter_contrib_nbextensions
42 | # RUN jupyter contrib nbextension install --user
43 | # enable the Nbextensions
44 | #RUN jupyter nbextension enable contrib_nbextensions_help_item/main
45 | #RUN jupyter nbextension enable autosavetime/main
46 | #RUN jupyter nbextension enable codefolding/main
47 | #RUN jupyter nbextension enable code_font_size/code_font_size
48 | #RUN jupyter nbextension enable code_prettify/code_prettify
49 | #RUN jupyter nbextension enable collapsible_headings/main
50 | #RUN jupyter nbextension enable comment-uncomment/main
51 | #RUN jupyter nbextension enable equation-numbering/main
52 | #RUN jupyter nbextension enable execute_time/ExecuteTime
53 | #RUN jupyter nbextension enable gist_it/main
54 | #RUN jupyter nbextension enable hide_input/main
55 | #RUN jupyter nbextension enable spellchecker/main
56 | #RUN jupyter nbextension enable toc2/main
57 | #RUN jupyter nbextension enable toggle_all_line_numbers/main
58 |
59 | CMD ["jupyter", "notebook", "--no-browser","--NotebookApp.token=''","--NotebookApp.password=''", "--allow-root"]
60 |
61 | RUN sudo groupadd -g 1020 airflow
62 | RUN sudo useradd -s /bin/false -u 1010 -g 1020 airflow
63 |
64 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Algotrading Stack
2 |
3 |
4 | ## Comenzando 🚀
5 |
6 | Uno de las actividades recurrentes es armar un maquina que disponga de todos los servicios necesarios para empezar a desarrollar o poner en produccion estrategias de algotrading, el objetivo de este proyecto es proveer toda la infraestructura necesaria sin necesidad de instalar software o componentes locales sino a traves de un entorno contenerizado y virtualizado con la tecnologia provista por docker.
7 |
8 | ### Pre-requisitos 📋
9 |
10 | Solo tienes que tenes instalado docker en una maquina (linux, obvio)
11 |
12 | Aqui encontraras una guia para instalar docker en Ubuntu 20.04
13 | https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-20-04-es
14 |
15 | Si insistes con Windows, quizas esta guia te pueda ayudar
16 | https://www.simplilearn.com/tutorials/docker-tutorial/install-docker-on-windows
17 |
18 | Luego toda la instalación esta armada con un stack completo e integrado de docker, que utiliza partir de utilizar comando docker-compose arma todo el stack de microservicios
19 |
20 | ### Instalación 🔧
21 |
22 | La instalación es muy sencilla, veamos...
23 |
24 | 1) Copiar todo el contenido en una carpeta, ejemplo 'algotrading'
25 |
26 | 2) Edita las variables del archivo .env, ya que se uilizan en el archivo docker-compose.yaml para configurar los servicios de los contenedores
27 |
28 | WD="directorio de trabajo, donde reside esta misma carpeta, en el ejemplo seria la ruta completa de 'algotrading'"
29 |
30 | ND="directorio donde se alojaron los notebooks y demas archivos python que seran accesibles desde los contenedores"
31 |
32 | TWSUSERID="usuario del gateway de Interactive Brokers"
33 |
34 | TWSPASSWORD="password del gateway de Interactive Brokers"
35 |
36 | TRADING_MODE="modalidad del gateway de Interactive Brokers paper o live"
37 |
38 | TZ="timezone del gateway de Interactive Brokers"
39 |
40 | CUSTOM_USER="usuario de Metatrader Web"
41 |
42 | PASSWORD="password de Metatrader Web, si se deja en blanco entonces no se pedira autenticacion"
43 |
44 | 3) Debes crear la network algotrading_stack_default en docker con la subnet correspondiente para asignar las IPs correspondientes a cada servicio
45 |
46 | 4) Ejecutar el script
47 | ./starter_script.sh
48 |
49 | ### Gestión de los contenedores 🔩
50 |
51 | Para ver el status y administrar los contenedores puedes usar el comando docker-compose o tambien la interfaz web provista por Portainer:
52 |
53 | * Portainer: http://localhost:9000
54 |
55 | La primera vez que ingreses te va a solicitar crear una password para la cuenta admin, y luego tendras acceso a la gestión y administracion de los contenedores, acceder a las consolas de los mismos, monitorear recursos y status de los servicios.
56 |
57 | ### Servicios incluidos ⌨️
58 |
59 | Los diferentes servicios del stack se pueden acceder a traves:
60 |
61 | * Airflow: http://localhost:8080
62 | * Superset: http://localhost:8088
63 | * Celery Flower: http://localhost:5555
64 | * PgAdmin: http://localhost:1234
65 | * Metatrader http://localhost:3000
66 | mt5linux via host localhost port 8001
67 | * IBGateway via vnc localhost:5999
68 |
69 | ## Paquetes y librerias 📦
70 |
71 | En los archivos requirements.txt encontraras las librerias que se han incluido, he incluido entre otras:
72 |
73 | * sklearn
74 | * xgboost
75 | * ib_insync
76 | * backtrader2 (fork de backtrader con bugfixes)
77 | * vectorbt
78 | * ffn
79 | * pyfolio
80 | * quantstats
81 | * riskfolio-lib
82 | * mt5linux
83 |
84 | ## Agradecimientos 📢
85 |
86 | * A @paduel por la inspiración y el empujoncito en armar este stack 🤓
87 | * Basado en las ideas y en el trabajo de @saeed349
88 | https://github.com/saeed349/Microservices-Based-Algorithmic-Trading-System
89 | * Para Metatrader he integrado la imagen desarrollada por @gmag11
90 | https://github.com/gmag11/MetaTrader5-Docker-Image
91 |
92 |
93 |
94 |
--------------------------------------------------------------------------------
/dockerfile_mt5/Metatrader/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # Configuration variables
4 | mt5file='/config/.wine/drive_c/Program Files/MetaTrader 5/terminal64.exe'
5 | WINEPREFIX='/config/.wine'
6 | wine_executable="wine"
7 | metatrader_version="5.0.4874"
8 | mt5server_port="8001"
9 | mono_url="https://dl.winehq.org/wine/wine-mono/10.0.0/wine-mono-10.0.0-x86.msi"
10 | git_url="https://github.com/git-for-windows/git/releases/download/v2.49.0.windows.1/Git-2.49.0-64-bit.exe"
11 | python_url="https://www.python.org/ftp/python/3.11.9/python-3.11.9-amd64.exe"
12 | mt5setup_url="https://download.mql5.com/cdn/web/metaquotes.software.corp/mt5/mt5setup.exe"
13 |
14 | export WINEDEBUG=-all
15 | # Function to display a graphical message
16 | show_message() {
17 | echo $1
18 | }
19 |
20 | # Function to check if a dependency is installed
21 | check_dependency() {
22 | if ! command -v $1 &> /dev/null; then
23 | echo "$1 is not installed. Please install it to continue."
24 | exit 1
25 | fi
26 | }
27 |
28 | # Function to check if a Python package is installed
29 | is_python_package_installed() {
30 | python3 -c "import pkg_resources; exit(not pkg_resources.require('$1'))" 2>/dev/null
31 | return $?
32 | }
33 |
34 | # Function to check if a Python package is installed in Wine
35 | is_wine_python_package_installed() {
36 | $wine_executable python -c "import pkg_resources; exit(not pkg_resources.require('$1'))" 2>/dev/null
37 | return $?
38 | }
39 |
40 | # Check for necessary dependencies
41 | check_dependency "curl"
42 | check_dependency "$wine_executable"
43 |
44 | # Install Mono if not present
45 | if [ ! -e "/config/.wine/drive_c/windows/mono" ]; then
46 | show_message "[1/7] Downloading and installing Mono..."
47 | curl -o /config/.wine/drive_c/mono.msi $mono_url
48 | WINEDLLOVERRIDES=mscoree=d $wine_executable msiexec /i /config/.wine/drive_c/mono.msi /qn
49 | rm /config/.wine/drive_c/mono.msi
50 | show_message "[1/7] Mono installed."
51 | else
52 | show_message "[1/7] Mono is already installed."
53 | fi
54 |
55 | # Check if MetaTrader 5 is already installed
56 | if [ -e "$mt5file" ]; then
57 | show_message "[2/7] File $mt5file already exists."
58 | else
59 | show_message "[2/7] File $mt5file is not installed. Installing..."
60 |
61 | # Fix for ucrtbase.dll.crealf
62 | winetricks -q vcrun2015
63 | # Set Windows 10 mode in Wine and download and install MT5
64 | $wine_executable reg add "HKEY_CURRENT_USER\\Software\\Wine" /v Version /t REG_SZ /d "win10" /f
65 | show_message "[3/7] Downloading MT5 installer..."
66 | curl -o /config/.wine/drive_c/mt5setup.exe $mt5setup_url
67 | show_message "[3/7] Installing MetaTrader 5..."
68 | $wine_executable "/config/.wine/drive_c/mt5setup.exe" "/auto" &
69 | wait
70 | rm -f /config/.wine/drive_c/mt5setup.exe
71 | fi
72 |
73 | # Recheck if MetaTrader 5 is installed
74 | if [ -e "$mt5file" ]; then
75 | show_message "[4/7] File $mt5file is installed. Running MT5..."
76 | $wine_executable "$mt5file" &
77 | else
78 | show_message "[4/7] File $mt5file is not installed. MT5 cannot be run."
79 | fi
80 |
81 |
82 | # Install Python in Wine if not present
83 | #if ! $wine_executable python --version 2>/dev/null; then
84 | if ! $wine_executable python -c 'import sys; assert sys.version_info >= (3,11)' > /dev/null; then
85 | show_message "[5/7] Installing Python in Wine..."
86 | curl -L $python_url -o /tmp/python-installer.exe
87 | $wine_executable /tmp/python-installer.exe /quiet InstallAllUsers=1 PrependPath=1
88 | rm /tmp/python-installer.exe
89 | show_message "[5/7] Python installed in Wine."
90 | else
91 | show_message "[5/7] Python is already installed in Wine."
92 | fi
93 |
94 | # Upgrade pip and install required packages
95 | show_message "[6/7] Installing Python libraries"
96 | $wine_executable python -m pip install --upgrade --no-cache-dir pip
97 | # Install MetaTrader5 library in Windows if not installed
98 | show_message "[6/7] Installing MetaTrader5 library in Windows"
99 | if ! is_wine_python_package_installed "MetaTrader5==$metatrader_version"; then
100 | $wine_executable python -m pip install --no-cache-dir MetaTrader5==$metatrader_version
101 | fi
102 |
103 | # Install git in Windows if not installed
104 | show_message "[6/7] Checking and installing git in Windows if necessary"
105 | if ! $wine_executable git --version 2>&1 >/dev/null; then
106 | curl -L $git_url -o /tmp/git-installer.exe
107 | $wine_executable /tmp/git-installer.exe /verysilent
108 | fi
109 |
110 | # Install mt5linux library in Windows if not installed
111 | show_message "[6/7] Checking and installing mt5linux library in Windows if necessary"
112 | if ! is_wine_python_package_installed "mt5linux"; then
113 | $wine_executable python -m pip install --no-cache-dir git+https://github.com/ultra1971/mt5linux.git --ignore-requires-python
114 | fi
115 |
116 | # Install mt5linux library in Linux if not installed
117 | show_message "[6/7] Checking and installing mt5linux library in Linux if necessary"
118 | if ! is_python_package_installed "mt5linux"; then
119 | pip install git+https://github.com/ultra1971/mt5linux.git --ignore-requires-python --break-system-packages
120 | fi
121 |
122 | # Install pyxdg library in Linux if not installed
123 | show_message "[6/7] Checking and installing pyxdg library in Linux if necessary"
124 | if ! is_python_package_installed "pyxdg"; then
125 | pip install pyxdg --break-system-packages
126 | fi
127 |
128 | # Start the MT5 server on Linux
129 | show_message "[7/7] Starting the mt5linux server..."
130 | python3 -m mt5linux --host 0.0.0.0 -p $mt5server_port -w $wine_executable python.exe &
131 |
132 | # Give the server some time to start
133 | sleep 5
134 |
135 | # Check if the server is running
136 | if ss -tuln | grep ":$mt5server_port" > /dev/null; then
137 | show_message "[7/7] The mt5linux server is running on port $mt5server_port."
138 | else
139 | show_message "[7/7] Failed to start the mt5linux server on port $mt5server_port."
140 | fi
141 |
--------------------------------------------------------------------------------
/docker-compose.yaml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | x-airflow-common:
4 | &airflow-common
5 | build:
6 | context: ./dockerfile_airflow
7 | environment:
8 | &airflow-common-env
9 | AIRFLOW__CORE__EXECUTOR: CeleryExecutor
10 | AIRFLOW__CORE__SQL_ALCHEMY_CONN: postgresql+psycopg2://airflow:airflow@pg-airflow/airflow
11 | AIRFLOW__CELERY__RESULT_BACKEND: db+postgresql://airflow:airflow@pg-airflow/airflow
12 | AIRFLOW__CELERY__BROKER_URL: redis://:@redis:6379/0
13 | AIRFLOW__CORE__FERNET_KEY: ''
14 | AIRFLOW__CORE__DAGS_ARE_PAUSED_AT_CREATION: 'true'
15 | AIRFLOW__CORE__LOAD_EXAMPLES: 'false'
16 | TZ: ${TZ}
17 | volumes:
18 | - ${WD}/airflow/dags:/opt/airflow/dags
19 | - ${WD}/airflow/logs:/opt/airflow/logs
20 | - ${WD}/airflow/plugins:/opt/airflow/plugins
21 | - ${ND}:/home/airflow/notebooks
22 | - /home/algotrading:/home/airflow/algotrading
23 | user: "${AIRFLOW_UID:-50000}:${AIRFLOW_GID:-50000}"
24 | depends_on:
25 | redis:
26 | condition: service_healthy
27 | pg-airflow:
28 | condition: service_healthy
29 |
30 | services:
31 |
32 | portainer:
33 | container_name: "portainer"
34 | image: portainer/portainer-ce:latest
35 | command: -H unix:///var/run/docker.sock
36 | restart: always
37 | ports:
38 | - 9000:9000
39 | - 8000:8000
40 | environment:
41 | TZ: ${TZ}
42 | #healthcheck:
43 | # test: ["CMD", "curl", "--fail", "http://localhost:9000/api/status"]
44 | # interval: 60s
45 | # timeout: 5s
46 | # retries: 3
47 | volumes:
48 | - /var/run/docker.sock:/var/run/docker.sock
49 | - portainer-volume:/data
50 | networks:
51 | default:
52 | ipv4_address: 172.20.0.2
53 |
54 | redis:
55 | image: redis
56 | container_name: "redis"
57 | volumes:
58 | - redis-volume:/data
59 | ports:
60 | - 6379:6379
61 | environment:
62 | TZ: ${TZ}
63 | healthcheck:
64 | test: ["CMD", "redis-cli", "ping"]
65 | interval: 60s
66 | timeout: 30s
67 | retries: 10
68 | restart: always
69 | networks:
70 | default:
71 | ipv4_address: 172.20.0.3
72 |
73 | reverseproxy:
74 | container_name: "reverseproxy"
75 | build:
76 | context: ./dockerfile_reverseproxy
77 | ports:
78 | - 80:80
79 | healthcheck:
80 | test: ["CMD", "curl", "--silent", "--fail", "http://localhost:80/health"]
81 | interval: 60s
82 | timeout: 30s
83 | retries: 5
84 | environment:
85 | TZ: ${TZ}
86 | volumes:
87 | - ${ND}:/etc/nginx/html/
88 | restart: always
89 | networks:
90 | default:
91 | ipv4_address: 172.20.0.4
92 |
93 | pg-airflow:
94 | image: postgres
95 | container_name: "pg-airflow"
96 | environment:
97 | POSTGRES_USER: airflow
98 | POSTGRES_PASSWORD: airflow
99 | POSTGRES_DB: airflow
100 | TZ: ${TZ}
101 | ports:
102 | - 5432:5432
103 | volumes:
104 | - pg-airflow-volume:/var/lib/postgresql/data
105 | healthcheck:
106 | test: ["CMD", "pg_isready", "-U", "airflow"]
107 | interval: 5s
108 | retries: 5
109 | restart: always
110 | networks:
111 | default:
112 | ipv4_address: 172.20.0.5
113 |
114 | pg-master:
115 | image: postgres:13
116 | restart: always
117 | container_name: "pg-master"
118 | ports:
119 | - 5431:5431
120 | environment:
121 | SHARED_PASSWORD: password
122 | POSTGRES_PASSWORD: posgres349
123 | TZ: ${TZ}
124 | volumes:
125 | - ${WD}/postgress_db/scripts/:/docker-entrypoint-initdb.d/
126 | - pg-master-volume:/var/lib/postgresql/data
127 | healthcheck:
128 | test: ["CMD", "pg_isready", "-U", "postgres"]
129 | interval: 5s
130 | retries: 5
131 | restart: always
132 | networks:
133 | default:
134 | ipv4_address: 172.20.0.6
135 |
136 | pgadmin:
137 | image: dpage/pgadmin4
138 | container_name: "pg-admin"
139 | environment:
140 | PGADMIN_DEFAULT_EMAIL: "guest@guest.com"
141 | PGADMIN_DEFAULT_PASSWORD: "guest"
142 | TZ: ${TZ}
143 | volumes:
144 | - ${WD}/pgadmin/:/var/lib/pgadmin
145 | ports:
146 | - 1234:80
147 | depends_on:
148 | - reverseproxy
149 | depends_on:
150 | - pg-master
151 | networks:
152 | default:
153 | ipv4_address: 172.20.0.7
154 |
155 | airflow-scheduler:
156 | <<: *airflow-common
157 | container_name: "airflow-scheduler"
158 | command: scheduler
159 | restart: always
160 | networks:
161 | default:
162 | ipv4_address: 172.20.0.10
163 |
164 | airflow-worker:
165 | <<: *airflow-common
166 | container_name: "airflow-worker"
167 | command: celery worker
168 | restart: always
169 | networks:
170 | default:
171 | ipv4_address: 172.20.0.11
172 |
173 | airflow-webserver:
174 | <<: *airflow-common
175 | container_name: "airflow-webserver"
176 | command: webserver
177 | ports:
178 | - 8080:8080
179 | depends_on:
180 | - reverseproxy
181 | - redis
182 | - airflow-worker
183 | - airflow-scheduler
184 | - pg-airflow
185 | healthcheck:
186 | test: ["CMD", "curl", "--fail", "http://localhost:8080/health"]
187 | interval: 30s
188 | timeout: 30s
189 | retries: 5
190 | restart: always
191 | networks:
192 | default:
193 | ipv4_address: 172.20.0.12
194 |
195 | airflow-flower:
196 | <<: *airflow-common
197 | container_name: "airflow-flower"
198 | command: celery flower
199 | ports:
200 | - 5555:5555
201 | healthcheck:
202 | test: ["CMD", "curl", "--fail", "http://localhost:5555/"]
203 | interval: 30s
204 | timeout: 30s
205 | retries: 5
206 | restart: always
207 | networks:
208 | default:
209 | ipv4_address: 172.20.0.13
210 |
211 | airflow-init:
212 | <<: *airflow-common
213 | container_name: "airflow-init"
214 | command: version
215 | environment:
216 | <<: *airflow-common-env
217 | _AIRFLOW_DB_UPGRADE: 'true'
218 | _AIRFLOW_WWW_USER_CREATE: 'true'
219 | _AIRFLOW_WWW_USER_USERNAME: ${_AIRFLOW_WWW_USER_USERNAME:-airflow}
220 | _AIRFLOW_WWW_USER_PASSWORD: ${_AIRFLOW_WWW_USER_PASSWORD:-airflow}
221 | networks:
222 | default:
223 | ipv4_address: 172.20.0.19
224 |
225 | ib-gateway:
226 | container_name: "ib-gateway"
227 | build:
228 | context: ./ibgateway
229 | restart: always
230 | ports:
231 | - "7497:7497"
232 | - "4001:4001"
233 | - "5999:5999"
234 | environment:
235 | TZ: ${TZ}
236 | VNC_PASSWORD: ib
237 | VNC_PORT: 5999
238 | TWSUSERID: ${TWSUSERID}
239 | TWSPASSWORD: ${TWSPASSWORD}
240 | TRADING_MODE: ${TRADING_MODE}
241 | networks:
242 | default:
243 | ipv4_address: 172.20.0.20
244 |
245 | mt5:
246 | container_name: "mt5"
247 | build:
248 | context: ./dockerfile_mt5
249 | restart: always
250 | volumes:
251 | - ${WD}/config/:/config
252 | healthcheck:
253 | test: ["CMD", "curl", "--fail", "http://localhost:3000"]
254 | interval: 30s
255 | timeout: 30s
256 | retries: 5
257 | ports:
258 | - "3000:3000"
259 | - "8001:8001"
260 | environment:
261 | UID: 1000
262 | GID: 1000
263 | CUSTOM_USER: ${CUSTOM_USER}
264 | PASSWORD: ${PASSWORD}
265 | networks:
266 | default:
267 | ipv4_address: 172.20.0.21
268 |
269 | superset:
270 | container_name: "superset"
271 | build:
272 | context: ./dockerfile_superset
273 | restart: always
274 | depends_on:
275 | - redis
276 | environment:
277 | MAPBOX_API_KEY: ${MAPBOX_API_KEY}
278 | SUPERSET_HOME: /etc/superset
279 | SUPERSET_ENV: production
280 | SUPERSET_SECRET_KEY: TwGiSenUQ0oUGEQT+E1o6IZzWzNDClPLppLmamPR7DN7DXhqZHdYJuRA
281 | TZ: ${TZ}
282 | ports:
283 | - "8088:8088"
284 | depends_on:
285 | - reverseproxy
286 | volumes:
287 | - ${WD}/superset/:/etc/superset
288 | - ${WD}/superset/:/var/lib/superset
289 | - ${ND}:/home/airflow/notebooks
290 | depends_on:
291 | - pg-master
292 | networks:
293 | default:
294 | ipv4_address: 172.20.0.23
295 |
296 | volumes:
297 | redis-volume:
298 | external: false
299 | pg-master-volume:
300 | external: false
301 | pg-airflow-volume:
302 | external: false
303 | portainer-volume:
304 | external: false
305 |
306 | networks:
307 | default:
308 | external:
309 | name: algotrading_stack_default
310 |
311 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/ibgateway/components/ibc/ibc.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 | # IBController 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 IBController Startup
14 | # Settings below controls this.
15 |
16 |
17 | # 1. IBController Startup Settings
18 | # ----------------------------------
19 | #
20 | # IBController logs information during operating that can be useful when
21 | # diagnosing problems. If set to 'yes', all logging output from
22 | # IBController is to the console and may be directed into a file using
23 | # the standard > or >> command line redirection operators. If set to 'no',
24 | # output from IBController that is logged after it has loaded TWS appears
25 | # in the TWS logfile: this helps to correlate IBController log entries with
26 | # TWS activity. The default is 'no'.
27 |
28 | LogToConsole=yes
29 |
30 |
31 | # IBController may be used to start the IB Gateway for the FIX CTCI. This
32 | # setting must be set to 'yes' if you want to run the FIX CTCI gateway. The
33 | # default is 'no'.
34 |
35 | FIX=no
36 |
37 |
38 | # 2. Authentication Settings
39 | # ----------------------------
40 | #
41 | # TWS and the IB API gateway require a single username and password.
42 | # You may specify the username and password using the following settings:
43 | #
44 | # IbLoginId
45 | # IbPassword
46 | #
47 | # Alternatively, you can specify the username and password in the command
48 | # files used to start TWS or the Gateway, but this is not recommended for
49 | # security reasons.
50 | #
51 | # If you don't specify them, you will be prompted for them in the usual
52 | # login dialog when TWS starts (but whatever you have specified will be
53 | # included in the dialog automatically: for example you may specify the
54 | # username but not the password, and then you will be prompted for the
55 | # password via the login dialog). Note that if you specify either
56 | # the username or the password (or both) in the command file, then
57 | # IbLoginId and IbPassword settings defined in this file are ignored.
58 | #
59 | #
60 | # The FIX CTCI gateway requires one username and password for FIX order
61 | # routing, and optionally a separate username and password for market
62 | # data connections. You may specify the usernames and passwords using
63 | # the following settings:
64 | #
65 | # FIXLoginId
66 | # FIXPassword
67 | # IbLoginId (optional - for market data connections)
68 | # IbPassword (optional - for market data connections)
69 | #
70 | # Alternatively you can specify the FIX username and password in the
71 | # command file used to start the FIX CTCI Gateway, but this is not
72 | # recommended for security reasons.
73 | #
74 | # If you don't specify them, you will be prompted for them in the usual
75 | # login dialog when FIX CTCI gateway starts (but whatever you have
76 | # specified will be included in the dialog automatically: for example
77 | # you may specify the usernames but not the passwords, and then you will
78 | # be prompted for the passwords via the login dialog). Note that if you
79 | # specify either the FIX username or the FIX password (or both) on the
80 | # command line, then FIXLoginId and FIXPassword settings defined in this
81 | # file are ignored; he same applies to the market data username and
82 | # password.
83 |
84 | #
85 | # Passwords may be specified in an 'encrypted' form. This makes
86 | # it tricky (but by no means impossible) for someone looking at this
87 | # file to know what your password is. It is recommended that you don't
88 | # use this feature, because the protection it gives is minimal. Instead
89 | # you should make sure that this .ini file is protected by your
90 | # operating system from unauthorised access, and prefereably it
91 | # should be encrypted.
92 | #
93 | # Use these settings to indicate whether the password(s) are encrypted:
94 | #
95 | # PasswordEncrypted
96 | # FIXPasswordEncrypted
97 | #
98 | # If you want to use the encrypted form, set the relevant setting to
99 | # 'yes', otherwise set it to 'no'. If you omit this setting, 'yes' is
100 | # assumed.
101 | #
102 | # To get the encrypted form, edit the IBControllerEncrypt.bat file
103 | # and replace "aaaaa" with your password; then run
104 | # IBControllerEncrypt.bat to display the encrypted version of
105 | # your password.
106 | #
107 |
108 | # IB API Authentication Settings
109 | # ------------------------------
110 |
111 | # Your TWS username:
112 |
113 | IbLoginId=edemo
114 |
115 |
116 | # Your TWS password (encrypted if appropriate):
117 |
118 | IbPassword=demouser
119 | PasswordEncrypted=no
120 |
121 |
122 | # FIX CTCI Authentication Settings
123 | # --------------------------------
124 |
125 | # Your FIX CTCI username:
126 |
127 | FIXLoginId=
128 |
129 |
130 | # Your FIX CTCI password (encrypted if appropriate):
131 |
132 | FIXPassword=
133 | FIXPasswordEncrypted=yes
134 |
135 |
136 | # Trading Mode
137 | # ------------
138 | #
139 | # TWS 955 introduced a new Trading Mode combo box on its login
140 | # dialog. This indicates whether the live account or the paper
141 | # trading account corresponding to the supplied credentials is
142 | # to be used. The allowed values are 'live' (the default) and
143 | # 'paper'. Note that if the supplied credentials are for a
144 | # paper trading account, the setting is ignored. For earlier
145 | # versions of TWS this setting has no effect.
146 |
147 | #TradingMode=live
148 | TradingMode=paper
149 |
150 |
151 | # 3. TWS Startup Settings
152 | # -------------------------
153 | #
154 | # Path to the directory where TWS should store its settings. This is
155 | # normally the folder in which TWS is installed. However you may set
156 | # it to some other location if you wish (for example if you want to
157 | # run multiple instances of TWS with different settings).
158 | #
159 | # It is recommended for clarity that you use an absolute path. The
160 | # effect of using a relative path is undefined.
161 | #
162 | # Linux and OS X users should use the appropriate path syntax.
163 | #
164 | # Note that, for Windows users, you MUST use double separator
165 | # characters to separate the elements of the folder path: for
166 | # example, IbDir=C:\\IBLiveSettings is valid, but
167 | # IbDir=C:\IBLiveSettings is NOT valid and will give unexpected
168 | # results. Linux and OS X users need not use double separators,
169 | # but they are acceptable.
170 | #
171 | # The default is the current working directory when IBController is
172 | # started.
173 |
174 | IbDir=/root/Jts
175 |
176 |
177 | # If you wish to store your TWS settings on IB's servers rather
178 | # than locally on your computer, set this to 'yes'
179 |
180 | StoreSettingsOnServer=no
181 |
182 |
183 | # Set to 'yes' to minimise TWS when it starts:
184 |
185 | MinimizeMainWindow=no
186 |
187 |
188 | # When TWS logs on it checks to see whether the account is already
189 | # logged in at another computer. If so it displays a dialog: this
190 | # setting instructs TWS how to proceed. If set to 'primary', TWS
191 | # ends the existing session and continues with the new session. If
192 | # set to 'secondary', TWS exits so that the existing session is
193 | # unaffected. If set to 'manual', the user must handle the dialog.
194 | # Note that when set to 'primary', if another TWS session is
195 | # started and manually told to end the primary session, the primary
196 | # session is automatically reconnected (provided the other session
197 | # is in a TWS version higher than 949). Also note that if two
198 | # primary sessions are started, they will both keep reconnecting
199 | # and disconnecting the other: therefore care needs to be exercised
200 | # in the use of this setting. The default is 'manual'.
201 |
202 | ExistingSessionDetectedAction=primary
203 |
204 |
205 | # If set to 'accept', IBController automatically accepts incoming
206 | # API connection dialogs. If set to 'reject', IBController
207 | # automatically rejects incoming API connection dialogs. If
208 | # set to 'manual', the user must decide whether to accept or reject
209 | # incoming API connection dialogs. The default is 'accept'.
210 | # NB: it is recommended to set this to 'reject', and to explicitly
211 | # configure which IP addresses can connect to the API in TWS's API
212 | # configuration page, as this is much more secure (in this case, no
213 | # incoming API connection dialogs will occur for those IP addresses).
214 |
215 | AcceptIncomingConnectionAction=accept
216 |
217 |
218 | # NB: ShowAllTrades is deprecated starting with TWS 955. This is
219 | # because IB have changed the Trades window in such a way that it
220 | # is no longer possible to programmatically set the desired option.
221 | # However the user's setting is now persisted within and across
222 | # TWS sessions, so ShowAllTrades is no longer really needed. If
223 | # ShowAllTrades is set to 'yes', it will have no observable effect.
224 | #
225 | # The following description applies to TWS versions BEFORE TWS 955:
226 | #
227 | # If ShowAllTrades is set to 'yes', IBController causes TWS to
228 | # display the Trades log at startup, and sets the 'All' checkbox
229 | # to ensure that the API reports all executions that have occurred
230 | # during the past week. Moreover, any attempt by the user to change
231 | # any of the 'Show trades' checkboxes is ignored; similarly if the
232 | # user closes the Trades log, it is immediately re-displayed with
233 | # the 'All' checkbox set. If set to 'no', IBController does not
234 | # interact with the Trades log, and only the current session's
235 | # executions are returned via the API (unless the user changes the
236 | # Trades log checkboxes). The default is 'no'.
237 |
238 | ShowAllTrades=no
239 |
240 |
241 | # If ForceTwsApiPort is set to an integer, it forces IBController to
242 | # change the TWS API Socket Port to that number shortly after
243 | # startup. This setting is most commonly required if you are running
244 | # multiple IBController instances for different accounts and would
245 | # like different ports to be configured automatically (ie without you
246 | # needing do this manually in the TWS window that IBController would
247 | # open). Leaving the setting blank will make no change, so 4001 or
248 | # 7496 would be the usual default (or whatever you manually change
249 | # the port number to).
250 |
251 | ForceTwsApiPort=4002
252 |
253 |
254 | # If ReadOnlyLogin is set to 'yes', and the user is enrolled in IB's
255 | # account security programme, the user will not be asked to supply
256 | # the security code, and login to TWS will occur automatically in
257 | # read-only mode: in this mode, placing or managing orders is not
258 | # allowed. If set to 'no', and the user is enrolled in IB's account
259 | # security programme, the user must supply the relevant security
260 | # code to complete the login. If the user is not enrolled in IB's
261 | # account security programme, this setting is ignored. The default
262 | # is 'no'.
263 |
264 | ReadOnlyLogin=no
265 |
266 |
267 | # Logging in to a paper-trading account results in TWS displaying
268 | # a dialog asking the user to confirm that they are aware that this
269 | # is not a brokerage account. Until this dialog has been accepted,
270 | # TWS will not allow API connections to succeed. Setting this
271 | # to 'yes' (the default) will cause IBController to automatically
272 | # confirm acceptance. Setting it to 'no' will leave the dialog
273 | # on display, and the user will have to deal with it manually.
274 |
275 | AcceptNonBrokerageAccountWarning=yes
276 |
277 |
278 | # 4. TWS Auto-Closedown
279 | # -----------------------
280 | #
281 | # Set to yes or no (lower case):
282 | #
283 | # yes means allow TWS to shut down automatically at its
284 | # specified shutdown time, which is set via the TWS
285 | # configuration menu.
286 | #
287 | # no means TWS never shuts down automatically.
288 | #
289 | # NB: IB recommends that you do not keep TWS running
290 | # continuously. If you set this setting to 'no', you may
291 | # experience incorrect TWS operation.
292 |
293 | IbAutoClosedown=no
294 |
295 |
296 |
297 | # 5. TWS Tidy Closedown Time
298 | # ----------------------------
299 | #
300 | # To tell IBController to tidily close TWS at a
301 | # specified day of the week and time, set this value
302 | # to
303 | # for example:
304 | # ClosedownAt=Friday 22:00
305 | #
306 | # Note that the day of the week must be specified using your
307 | # default locale. Also note that Java will only accept
308 | # characters encoded to ISO 8859-1 (Latin-1). This means that
309 | # if the day name in your default locale uses any non-Latin-1
310 | # characters you need to encode them using Unicode escapes
311 | # (see http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.3
312 | # for details). For example, to tidily close TWS at 12:00 on
313 | # Saturday where the default locale is Simplified Chinese,
314 | # use the following:
315 | # #ClosedownAt=\u661F\u671F\u516D 12:00
316 |
317 | ClosedownAt=
318 |
319 |
320 |
321 | # 6. Other TWS Settings
322 | # -----------------------
323 | #
324 | # If you attempt to place an order for a contract for which
325 | # you have no market data subscription, TWS displays a dialog
326 | # to warn you against such blind trading.
327 | #
328 | # yes means the dialog is dismissed as though the user had
329 | # clicked the 'Ok' button: this means that you accept
330 | # the risk and want the order to be submitted.
331 | #
332 | # no means the dialog remains on display and must be
333 | # handled by the user.
334 |
335 | AllowBlindTrading=no
336 |
337 |
338 | # Indian versions of TWS may display a password expiry
339 | # notification dialog and a NSE Compliance dialog. These can be
340 | # dismissed by setting the following to yes. By default the
341 | # password expiry notice is not dismissed, but the NSE Compliance
342 | # notice is dismissed.
343 |
344 | # Warning: setting DismissPasswordExpiryWarning=yes will mean
345 | # you will not be notified when your password is about to expire.
346 | # You must then take other measures to ensure that your password
347 | # is changed within the expiry period, otherwise IBController will
348 | # not be able to login successfully.
349 |
350 | DismissPasswordExpiryWarning=no
351 | DismissNSEComplianceNotice=yes
352 |
353 |
354 | # Since TWS 906, orders placed using the BookTrader have to be confirmed
355 | # via a dialog box (this was due to a legal dispute between IB and Trading
356 | # Technologies who claimed IB were infringing a patent). If
357 | # AutoConfirmOrders=yes, then when orders are placed using the BookTrader,
358 | # the confirmation dialog is automatically handled, thereby effectively
359 | # restoring the one-click trading. The default is 'no', requiring the user
360 | # to manually confirm each trade.
361 | #
362 | # NB: this setting has been removed as the dispute has been resolved and
363 | # TWS users now have the option to request the order confirmation dialog
364 | # to not be displayed.
365 | #
366 | #AutoConfirmOrders=no
367 |
368 |
369 | # You can tell TWS to automatically save its settings on a schedule
370 | # of your choosing. You can specify one of more specific times,
371 | # like this:
372 | #
373 | # SaveTwsSettingsAt=HH;MM [ HH:MM]...
374 | #
375 | # for example:
376 | # SaveTwsSettingsAt=08:00 12:30 17:30
377 | #
378 | # Or you can specify an interval at which settings are to be saved,
379 | # optionally starting at a specific time and continuing until another
380 | # time, like this:
381 | #
382 | #SaveTwsSettingsAt=Every n [{mins | hours}] [hh:mm] [hh:mm]
383 | #
384 | # where the first hh:mm is the shart time and the second is the end
385 | # time. If you don't specify the end time, settings are saved regularly
386 | # from the start time till midnight. If you don't specify the start time.
387 | # settings are saved reularly all day, beginning at 00:00. Note that
388 | # settings will always be saved at the end time, even if that is not
389 | # exactly one interval later than the previous time. Examples:
390 | #
391 | # To save every 30 minutes all day starting at 00:00
392 | #SaveTwsSettingsAt=Every 30
393 | #SaveTwsSettingsAt=Every 30 mins
394 | #
395 | # To save every hour starting at 08:00 and ending at midnight
396 | #SaveTwsSettingsAt=Every 1 hours 08:00
397 | #SaveTwsSettingsAt=Every 1 hours 08:00 00:00
398 | #
399 | # To save every 90 minutes starting at 08:00 up to and including 17:43
400 | #SaveTwsSettingsAt=Every 90 08:00 17:43
401 |
402 | SaveTwsSettingsAt=
403 |
404 |
405 |
406 | # 6. IBController Server Settings
407 | # ---------------------------------
408 | #
409 | # Do NOT CHANGE THE FOLLOWING SETTINGS unless you
410 | # intend to issue commands to IBController (for example
411 | # using telnet). Note that these settings have nothing to
412 | # do with running programs that use the TWS API.
413 |
414 | # The port that IBController listens on for commands
415 | # such as "STOP". DO NOT set this to the port number
416 | # used for TWS API connections. There is no good reason
417 | # to change this setting unless the port is used by
418 | # some other application.
419 |
420 | IbControllerPort=7462
421 |
422 |
423 | # A comma separated list of ip addresses, or host names,
424 | # which are allowed addresses for sending commands to
425 | # IBController. Commands can always be sent from the
426 | # same host as IBController is running on.
427 |
428 | IbControlFrom=
429 |
430 |
431 | # Specifies the IP address on which the IBController Server
432 | # is to listen. For a multi-homed host, this can be used
433 | # to specify that connection requests are only to be
434 | # accepted on the specified address. The default is to
435 | # accept connection requests on all local addresses.
436 |
437 | IbBindAddress=0.0.0.0
438 |
439 |
440 | # The specified string is output by the server when
441 | # the connection is first opened and after the completion
442 | # of each command. This can be useful if sending commands
443 | # using an interactive program such as telnet. The default
444 | # is that no prompt is output.
445 | # For example:
446 | #
447 | # CommandPrompt=>
448 |
449 | CommandPrompt=
450 |
451 |
452 | # Some commands can return intermediate information about
453 | # their progress. This setting controls whether such
454 | # information is sent. The default is that such information
455 | # is not sent.
456 |
457 | SuppressInfoMessages=yes
458 |
459 |
460 |
461 | # 7. Diagnostic Settings
462 | # ------------------------
463 |
464 | # If LogComponents is set to 'open' or 'yes' or 'true',
465 | # IBController logs information about the structure of each
466 | # TWS window it detects the first time it is encountered. If
467 | # set to 'activate', the information is logged every time
468 | # a TWS window is made active. If set to 'never' or 'no' or
469 | # 'false', this information is never logged. The default is
470 | # 'never'.
471 | #
472 | # The logged information shows the hierarchical organisation
473 | # of all the components of the window, and includes the
474 | # current values of text boxes and labels.
475 | #
476 | # Note that when set to 'open', 'activate' or'yes', there is
477 | # a small performance impact due to the extra logging. Also
478 | # logfile size may be significantly increased, especially if
479 | # set to 'activate' and the user is actively using TWS. It
480 | # is therefore recommended that the setting be left at 'no'
481 | # unless there is a specific reason that this information is
482 | # needed.
483 |
484 | LogComponents=never
485 |
--------------------------------------------------------------------------------
/ibgateway/components/ibc/config.ini:
--------------------------------------------------------------------------------
1 | # Note that in the comments in this file, TWS refers to both the Trader
2 | # Workstation and the IB Gateway, unless explicitly stated otherwise.
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 |
18 | # =============================================================================
19 | # 1. IBC Startup Settings
20 | # =============================================================================
21 |
22 |
23 | # IBC may be used to start the IB Gateway for the FIX CTCI. This
24 | # setting must be set to 'yes' if you want to run the FIX CTCI gateway. The
25 | # default is 'no'.
26 |
27 | FIX=no
28 |
29 |
30 |
31 | # =============================================================================
32 | # 2. Authentication Settings
33 | # =============================================================================
34 |
35 | # TWS and the IB API gateway require a single username and password.
36 | # You may specify the username and password using the following settings:
37 | #
38 | # IbLoginId
39 | # IbPassword
40 | #
41 | # Alternatively, you can specify the username and password in the command
42 | # files used to start TWS or the Gateway, but this is not recommended for
43 | # security reasons.
44 | #
45 | # If you don't specify them, you will be prompted for them in the usual
46 | # login dialog when TWS starts (but whatever you have specified will be
47 | # included in the dialog automatically: for example you may specify the
48 | # username but not the password, and then you will be prompted for the
49 | # password via the login dialog). Note that if you specify either
50 | # the username or the password (or both) in the command file, then
51 | # IbLoginId and IbPassword settings defined in this file are ignored.
52 | #
53 | #
54 | # The FIX CTCI gateway requires one username and password for FIX order
55 | # routing, and optionally a separate username and password for market
56 | # data connections. You may specify the usernames and passwords using
57 | # the following settings:
58 | #
59 | # FIXLoginId
60 | # FIXPassword
61 | # IbLoginId (optional - for market data connections)
62 | # IbPassword (optional - for market data connections)
63 | #
64 | # Alternatively you can specify the FIX username and password in the
65 | # command file used to start the FIX CTCI Gateway, but this is not
66 | # recommended for security reasons.
67 | #
68 | # If you don't specify them, you will be prompted for them in the usual
69 | # login dialog when FIX CTCI gateway starts (but whatever you have
70 | # specified will be included in the dialog automatically: for example
71 | # you may specify the usernames but not the passwords, and then you will
72 | # be prompted for the passwords via the login dialog). Note that if you
73 | # specify either the FIX username or the FIX password (or both) on the
74 | # command line, then FIXLoginId and FIXPassword settings defined in this
75 | # file are ignored; he same applies to the market data username and
76 | # password.
77 |
78 | # IB API Authentication Settings
79 | # ------------------------------
80 |
81 | # Your TWS username:
82 |
83 | IbLoginId=edemo
84 |
85 |
86 | # Your TWS password:
87 |
88 | IbPassword=demouser
89 |
90 |
91 | # FIX CTCI Authentication Settings
92 | # --------------------------------
93 |
94 | # Your FIX CTCI username:
95 |
96 | FIXLoginId=
97 |
98 |
99 | # Your FIX CTCI password:
100 |
101 | FIXPassword=
102 |
103 |
104 | # Second Factor Authentication Timeout Settings
105 | # ---------------------------------------------
106 | #
107 | # If you use the IBKR Mobile app for second factor authentication,
108 | # and you fail to complete the process before the time limit imposed
109 | # by IBKR, you can use this setting to tell IBC to exit: arrangements
110 | # can then be made to automatically restart IBC in order to initiate
111 | # the login sequence afresh. Otherwise, manual intervention at TWS's
112 | # Second Factor Authentication dialog is needed to complete the
113 | # login.
114 | #
115 | # Permitted values are 'yes' and 'no'. The default is 'no'.
116 | #
117 | # Note that the scripts provided with the IBC zips for Windows and
118 | # Linux provide options to automatically restart in these
119 | # circumstances, but only if this setting is also set to 'yes'.
120 |
121 | ExitAfterSecondFactorAuthenticationTimeout=no
122 |
123 |
124 | # This setting is only relevant if
125 | # ExitAfterSecondFactorAuthenticationTimeout is set to 'yes'.
126 | #
127 | # It controls how long (in seconds) IBC waits for login to complete
128 | # after the user acknowledges the second factor authentication
129 | # alert at the IBKR Mobile app. If login has not completed after
130 | # this time, IBC terminates.
131 | # The default value is 40.
132 |
133 | SecondFactorAuthenticationExitInterval=
134 |
135 |
136 | # Trading Mode
137 | # ------------
138 | #
139 | # TWS 955 introduced a new Trading Mode combo box on its login
140 | # dialog. This indicates whether the live account or the paper
141 | # trading account corresponding to the supplied credentials is
142 | # to be used. The allowed values are 'live' (the default) and
143 | # 'paper'. For earlier versions of TWS this setting has no
144 | # effect.
145 |
146 | #TradingMode=live
147 | TradingMode=paper
148 |
149 |
150 | # Paper-trading Account Warning
151 | # -----------------------------
152 | #
153 | # Logging in to a paper-trading account results in TWS displaying
154 | # a dialog asking the user to confirm that they are aware that this
155 | # is not a brokerage account. Until this dialog has been accepted,
156 | # TWS will not allow API connections to succeed. Setting this
157 | # to 'yes' (the default) will cause IBC to automatically
158 | # confirm acceptance. Setting it to 'no' will leave the dialog
159 | # on display, and the user will have to deal with it manually.
160 |
161 | AcceptNonBrokerageAccountWarning=yes
162 |
163 |
164 | # Login Dialog Display Timeout
165 | #-----------------------------
166 | #
167 | # In some circumstances, starting TWS may result in failure to display
168 | # the login dialog. Restarting TWS may help to resolve this situation,
169 | # and IBC does this automatically.
170 | #
171 | # This setting controls how long (in seconds) IBC waits for the login
172 | # dialog to appear before restarting TWS.
173 | #
174 | # Note that in normal circumstances with a reasonably specified
175 | # computer the time to displaying the login dialog is typically less
176 | # than 20 seconds, and frequently much less. However many factors can
177 | # influence this, and it is unwise to set this value too low.
178 | #
179 | # The default value is 60.
180 |
181 | LoginDialogDisplayTimeout = 60
182 |
183 |
184 |
185 | # =============================================================================
186 | # 3. TWS Startup Settings
187 | # =============================================================================
188 |
189 | # Path to settings store
190 | # ----------------------
191 | #
192 | # Path to the directory where TWS should store its settings. This is
193 | # normally the folder in which TWS is installed. However you may set
194 | # it to some other location if you wish (for example if you want to
195 | # run multiple instances of TWS with different settings).
196 | #
197 | # It is recommended for clarity that you use an absolute path. The
198 | # effect of using a relative path is undefined.
199 | #
200 | # Linux and macOS users should use the appropriate path syntax.
201 | #
202 | # Note that, for Windows users, you MUST use double separator
203 | # characters to separate the elements of the folder path: for
204 | # example, IbDir=C:\\IBLiveSettings is valid, but
205 | # IbDir=C:\IBLiveSettings is NOT valid and will give unexpected
206 | # results. Linux and macOS users need not use double separators,
207 | # but they are acceptable.
208 | #
209 | # The default is the current working directory when IBC is
210 | # started.
211 |
212 | IbDir=/root/Jts
213 |
214 |
215 | # Store settings on server
216 | # ------------------------
217 | #
218 | # If you wish to store a copy of your TWS settings on IB's
219 | # servers as well as locally on your computer, set this to
220 | # 'yes': this enables you to run TWS on different computers
221 | # with the same configuration, market data lines, etc. If set
222 | # to 'no', running TWS on different computers will not share the
223 | # same settings. If no value is specified, TWS will obtain its
224 | # settings from the same place as the last time this user logged
225 | # in (whether manually or using IBC).
226 |
227 | StoreSettingsOnServer=no
228 |
229 |
230 | # Minimize TWS on startup
231 | # -----------------------
232 | #
233 | # Set to 'yes' to minimize TWS when it starts:
234 |
235 | MinimizeMainWindow=no
236 |
237 |
238 | # Existing Session Detected Action
239 | # --------------------------------
240 | #
241 | # When a user logs on to an IBKR account for trading purposes by any means, the
242 | # IBKR account server checks to see whether the account is already logged in
243 | # elsewhere. If so, a dialog is displayed to both the users that enables them
244 | # to determine what happens next. The 'ExistingSessionDetectedAction' setting
245 | # instructs TWS how to proceed when it displays this dialog:
246 | #
247 | # * If the new TWS session is set to 'secondary', the existing session continues
248 | # and the new session terminates. Thus a secondary TWS session can never
249 | # override any other session.
250 | #
251 | # * If the existing TWS session is set to 'primary', the existing session
252 | # continues and the new session terminates (even if the new session is also
253 | # set to primary). Thus a primary TWS session can never be overridden by
254 | # any new session).
255 | #
256 | # * If both the existing and the new TWS sessions are set to 'primaryoverride',
257 | # the existing session terminates and the new session proceeds.
258 | #
259 | # * If the existing TWS session is set to 'manual', the user must handle the
260 | # dialog.
261 | #
262 | # The difference between 'primary' and 'primaryoverride' is that a
263 | # 'primaryoverride' session can be overriden over by a new 'primary' session,
264 | # but a 'primary' session cannot be overriden by any other session.
265 | #
266 | # When set to 'primary', if another TWS session is started and manually told to
267 | # end the 'primary' session, the 'primary' session is automatically reconnected.
268 | #
269 | # The default is 'manual'.
270 |
271 | ExistingSessionDetectedAction=primary
272 |
273 |
274 | # Override TWS API Port Number
275 | # ----------------------------
276 | #
277 | # If OverrideTwsApiPort is set to an integer, IBC changes the
278 | # 'Socket port' in TWS's API configuration to that number shortly
279 | # after startup. Leaving the setting blank will make no change to
280 | # the current setting. This setting is only intended for use in
281 | # certain specialized situations where the port number needs to
282 | # be set dynamically at run-time: most users will never need it,
283 | # so don't use it unless you know you need it.
284 |
285 | OverrideTwsApiPort=4002
286 |
287 |
288 | # Read-only Login
289 | # ---------------
290 | #
291 | # If ReadOnlyLogin is set to 'yes', and the user is enrolled in IB's
292 | # account security programme, the user will not be asked to perform
293 | # the second factor authentication action, and login to TWS will
294 | # occur automatically in read-only mode: in this mode, placing or
295 | # managing orders is not allowed. If set to 'no', and the user is
296 | # enrolled in IB's account security programme, the user must perform
297 | # the relevant second factor authentication action to complete the
298 | # login.
299 |
300 | # If the user is not enrolled in IB's account security programme,
301 | # this setting is ignored. The default is 'no'.
302 |
303 | ReadOnlyLogin=no
304 |
305 |
306 | # Read-only API
307 | # -------------
308 | #
309 | # If ReadOnlyApi is set to 'yes', API programs cannot submit, modify
310 | # or cancel orders. If set to 'no', API programs can do these things.
311 | # If not set, the existing TWS/Gateway configuration is unchanged.
312 | # NB: this setting is really only supplied for the benefit of new TWS
313 | # or Gateway instances that are being automatically installed and
314 | # started without user intervention (eg Docker containers). Where
315 | # a user is involved, they should use the Global Configuration to
316 | # set the relevant checkbox (this only needs to be done once) and
317 | # not provide a value for this setting.
318 |
319 | ReadOnlyApi=no
320 |
321 |
322 |
323 | # =============================================================================
324 | # 4. TWS Auto-Closedown
325 | # =============================================================================
326 | #
327 | # IMPORTANT NOTE: Starting with TWS 974, this setting no longer
328 | # works properly, because IB have changed the way TWS handles its
329 | # autologoff mechanism.
330 | #
331 | # You should now configure the TWS autologoff time to something
332 | # convenient for you, and restart IBC each day.
333 | #
334 | # Alternatively, discontinue use of IBC and use the auto-relogin
335 | # mechanism within TWS 974 and later versions (note that the
336 | # auto-relogin mechanism provided by IB is not available if you
337 | # use IBC).
338 |
339 | # Set to yes or no (lower case).
340 | #
341 | # yes means allow TWS to shut down automatically at its
342 | # specified shutdown time, which is set via the TWS
343 | # configuration menu.
344 | #
345 | # no means TWS never shuts down automatically.
346 | #
347 | # NB: IB recommends that you do not keep TWS running
348 | # continuously. If you set this setting to 'no', you may
349 | # experience incorrect TWS operation.
350 | #
351 | # NB: the default for this setting is 'no'. Since this will
352 | # only work properly with TWS versions earlier than 974, you
353 | # should explicitly set this to 'yes' for version 974 and later.
354 |
355 | IbAutoClosedown=no
356 |
357 |
358 |
359 | # =============================================================================
360 | # 5. TWS Tidy Closedown Time
361 | # =============================================================================
362 | #
363 | # NB: starting with TWS 974 this is no longer a useful option
364 | # because both TWS and Gateway now have the same auto-logoff
365 | # mechanism, and IBC can no longer avoid this.
366 | #
367 | # Note that giving this setting a value does not change TWS's
368 | # auto-logoff in any way: any setting will be additional to the
369 | # TWS auto-logoff.
370 | #
371 | # To tell IBC to tidily close TWS at a specified time every
372 | # day, set this value to , for example:
373 | # ClosedownAt=22:00
374 | #
375 | # To tell IBC to tidily close TWS at a specified day and time
376 | # each week, set this value to , for example:
377 | # ClosedownAt=Friday 22:00
378 | #
379 | # Note that the day of the week must be specified using your
380 | # default locale. Also note that Java will only accept
381 | # characters encoded to ISO 8859-1 (Latin-1). This means that
382 | # if the day name in your default locale uses any non-Latin-1
383 | # characters you need to encode them using Unicode escapes
384 | # (see http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#3.3
385 | # for details). For example, to tidily close TWS at 12:00 on
386 | # Saturday where the default locale is Simplified Chinese,
387 | # use the following:
388 | # #ClosedownAt=\u661F\u671F\u516D 12:00
389 |
390 | ClosedownAt=
391 |
392 |
393 |
394 | # =============================================================================
395 | # 6. Other TWS Settings
396 | # =============================================================================
397 |
398 | # Accept Incoming Connection
399 | # --------------------------
400 | #
401 | # If set to 'accept', IBC automatically accepts incoming
402 | # API connection dialogs. If set to 'reject', IBC
403 | # automatically rejects incoming API connection dialogs. If
404 | # set to 'manual', the user must decide whether to accept or reject
405 | # incoming API connection dialogs. The default is 'manual'.
406 | # NB: it is recommended to set this to 'reject', and to explicitly
407 | # configure which IP addresses can connect to the API in TWS's API
408 | # configuration page, as this is much more secure (in this case, no
409 | # incoming API connection dialogs will occur for those IP addresses).
410 |
411 | AcceptIncomingConnectionAction=accept
412 |
413 |
414 | # Allow Blind Trading
415 | # -------------------
416 | #
417 | # If you attempt to place an order for a contract for which
418 | # you have no market data subscription, TWS displays a dialog
419 | # to warn you against such blind trading.
420 | #
421 | # yes means the dialog is dismissed as though the user had
422 | # clicked the 'Ok' button: this means that you accept
423 | # the risk and want the order to be submitted.
424 | #
425 | # no means the dialog remains on display and must be
426 | # handled by the user.
427 |
428 | AllowBlindTrading=no
429 |
430 |
431 | # Save Settings on a Schedule
432 | # ---------------------------
433 | #
434 | # You can tell TWS to automatically save its settings on a schedule
435 | # of your choosing. You can specify one or more specific times,
436 | # like this:
437 | #
438 | # SaveTwsSettingsAt=HH:MM [ HH:MM]...
439 | #
440 | # for example:
441 | # SaveTwsSettingsAt=08:00 12:30 17:30
442 | #
443 | # Or you can specify an interval at which settings are to be saved,
444 | # optionally starting at a specific time and continuing until another
445 | # time, like this:
446 | #
447 | #SaveTwsSettingsAt=Every n [{mins | hours}] [hh:mm] [hh:mm]
448 | #
449 | # where the first hh:mm is the start time and the second is the end
450 | # time. If you don't specify the end time, settings are saved regularly
451 | # from the start time till midnight. If you don't specify the start time.
452 | # settings are saved regularly all day, beginning at 00:00. Note that
453 | # settings will always be saved at the end time, even if that is not
454 | # exactly one interval later than the previous time. If neither 'mins'
455 | # nor 'hours' is specified, 'mins' is assumed. Examples:
456 | #
457 | # To save every 30 minutes all day starting at 00:00
458 | #SaveTwsSettingsAt=Every 30
459 | #SaveTwsSettingsAt=Every 30 mins
460 | #
461 | # To save every hour starting at 08:00 and ending at midnight
462 | #SaveTwsSettingsAt=Every 1 hours 08:00
463 | #SaveTwsSettingsAt=Every 1 hours 08:00 00:00
464 | #
465 | # To save every 90 minutes starting at 08:00 up to and including 17:43
466 | #SaveTwsSettingsAt=Every 90 08:00 17:43
467 |
468 | SaveTwsSettingsAt=
469 |
470 |
471 |
472 | # =============================================================================
473 | # 7. Settings Specific to Indian Versions of TWS
474 | # =============================================================================
475 |
476 | # Indian versions of TWS may display a password expiry
477 | # notification dialog and a NSE Compliance dialog. These can be
478 | # dismissed by setting the following to yes. By default the
479 | # password expiry notice is not dismissed, but the NSE Compliance
480 | # notice is dismissed.
481 |
482 | # Warning: setting DismissPasswordExpiryWarning=yes will mean
483 | # you will not be notified when your password is about to expire.
484 | # You must then take other measures to ensure that your password
485 | # is changed within the expiry period, otherwise IBC will
486 | # not be able to login successfully.
487 |
488 | DismissPasswordExpiryWarning=no
489 | DismissNSEComplianceNotice=yes
490 |
491 |
492 |
493 | # =============================================================================
494 | # 8. IBC Command Server Settings
495 | # =============================================================================
496 |
497 | # Do NOT CHANGE THE FOLLOWING SETTINGS unless you
498 | # intend to issue commands to IBC (for example
499 | # using telnet). Note that these settings have nothing to
500 | # do with running programs that use the TWS API.
501 |
502 | # Command Server Port Number
503 | # --------------------------
504 | #
505 | # The port number that IBC listens on for commands
506 | # such as "STOP". DO NOT set this to the port number
507 | # used for TWS API connections. There is no good reason
508 | # to change this setting unless the port is used by
509 | # some other application (typically another instance of
510 | # IBC). The default value is 0, which tells IBC not to
511 | # start the command server
512 |
513 | CommandServerPort=7462
514 |
515 |
516 | # Permitted Command Sources
517 | # -------------------------
518 | #
519 | # A comma separated list of IP addresses, or host names,
520 | # which are allowed addresses for sending commands to
521 | # IBC. Commands can always be sent from the
522 | # same host as IBC is running on.
523 |
524 | ControlFrom=
525 |
526 |
527 | # Address for Receiving Commands
528 | # ------------------------------
529 | #
530 | # Specifies the IP address on which the Command Server
531 | # is to listen. For a multi-homed host, this can be used
532 | # to specify that connection requests are only to be
533 | # accepted on the specified address. The default is to
534 | # accept connection requests on all local addresses.
535 |
536 | BindAddress=0.0.0.0
537 |
538 |
539 | # Command Prompt
540 | # --------------
541 | #
542 | # The specified string is output by the server when
543 | # the connection is first opened and after the completion
544 | # of each command. This can be useful if sending commands
545 | # using an interactive program such as telnet. The default
546 | # is that no prompt is output.
547 | # For example:
548 | #
549 | # CommandPrompt=>
550 |
551 | CommandPrompt=
552 |
553 |
554 | # Suppress Command Server Info Messages
555 | # -------------------------------------
556 | #
557 | # Some commands can return intermediate information about
558 | # their progress. This setting controls whether such
559 | # information is sent. The default is that such information
560 | # is not sent.
561 |
562 | SuppressInfoMessages=yes
563 |
564 |
565 |
566 | # =============================================================================
567 | # 9. Diagnostic Settings
568 | # =============================================================================
569 | #
570 | # IBC can log information about the structure of windows
571 | # displayed by TWS. This information is useful when adding
572 | # new features to IBC or when behaviour is not as expected.
573 | #
574 | # The logged information shows the hierarchical organisation
575 | # of all the components of the window, and includes the
576 | # current values of text boxes and labels.
577 | #
578 | # Note that this structure logging has a small performance
579 | # impact, and depending on the settings can cause the logfile
580 | # size to be significantly increased. It is therefore
581 | # recommended that the LogStructureWhen setting be set to
582 | # 'never' (the default) unless there is a specific reason
583 | # that this information is needed.
584 |
585 |
586 | # Scope of Structure Logging
587 | # --------------------------
588 | #
589 | # The LogStructureScope setting indicates which windows are
590 | # eligible for structure logging:
591 | #
592 | # - if set to 'known', only windows that IBC recognizes
593 | # are eligible - these are windows that IBC has some
594 | # interest in monitoring, usually to take some action
595 | # on the user's behalf;
596 | #
597 | # - if set to 'unknown', only windows that IBC does not
598 | # recognize are eligible. Most windows displayed by
599 | # TWS fall into this category;
600 | #
601 | # - if set to 'untitled', only windows that IBC does not
602 | # recognize and that have no title are eligible. These
603 | # are usually message boxes or similar small windows,
604 | #
605 | # - if set to 'all', then every window displayed by TWS
606 | # is eligible.
607 | #
608 | # The default value is 'known'.
609 |
610 | LogStructureScope=known
611 |
612 |
613 | # When to Log Window Structure
614 | # ----------------------------
615 | #
616 | # The LogStructureWhen setting specifies the circumstances
617 | # when eligible TWS windows have their structure logged:
618 | #
619 | # - if set to 'open' or 'yes' or 'true', IBC logs the
620 | # structure of an eligible window the first time it
621 | # is encountered;
622 | #
623 | # - if set to 'activate', the structure is logged every
624 | # time an eligible window is made active;
625 | #
626 | # - if set to 'never' or 'no' or 'false', structure
627 | # information is never logged.
628 | #
629 | # The default value is 'never'.
630 |
631 | LogStructureWhen=never
632 |
633 |
634 | # DEPRECATED SETTING
635 | # ------------------
636 | #
637 | # LogComponents - THIS SETTING WILL BE REMOVED IN A FUTURE
638 | # RELEASE
639 | #
640 | # If LogComponents is set to any value, this is equivalent
641 | # to setting LogStructureWhen to that same value and
642 | # LogStructureScope to 'all': the actual values of those
643 | # settings are ignored. The default is that the values
644 | # of LogStructureScope and LogStructureWhen are honoured.
645 |
646 | #LogComponents=
--------------------------------------------------------------------------------
/dockerfile_airflow/plot.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf-8; py-indent-offset:4 -*-
3 | ###############################################################################
4 | #
5 | # Copyright (C) 2015-2023 Daniel Rodriguez
6 | #
7 | # This program is free software: you can redistribute it and/or modify
8 | # it under the terms of the GNU General Public License as published by
9 | # the Free Software Foundation, either version 3 of the License, or
10 | # (at your option) any later version.
11 | #
12 | # This program is distributed in the hope that it will be useful,
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | # GNU General Public License for more details.
16 | #
17 | # You should have received a copy of the GNU General Public License
18 | # along with this program. If not, see .
19 | #
20 | ###############################################################################
21 | from __future__ import (absolute_import, division, print_function,
22 | unicode_literals)
23 |
24 | import bisect
25 | import collections
26 | import datetime
27 | import itertools
28 | import math
29 | import operator
30 | import sys
31 |
32 | import matplotlib
33 | import numpy as np # guaranteed by matplotlib
34 | import matplotlib.dates as mdates
35 | import matplotlib.font_manager as mfontmgr
36 | import matplotlib.legend as mlegend
37 | import matplotlib.ticker as mticker
38 |
39 | from ..utils.py3 import range, with_metaclass, string_types, integer_types
40 | from .. import AutoInfoClass, MetaParams, TimeFrame, date2num
41 |
42 | from .finance import plot_candlestick, plot_ohlc, plot_volume, plot_lineonclose
43 | from .formatters import (MyVolFormatter, MyDateFormatter, getlocator)
44 | from . import locator as loc
45 | from .multicursor import MultiCursor
46 | from .scheme import PlotScheme
47 | from .utils import tag_box_style
48 |
49 |
50 | class PInfo(object):
51 | def __init__(self, sch):
52 | self.sch = sch
53 | self.nrows = 0
54 | self.row = 0
55 | self.clock = None
56 | self.x = None
57 | self.xlen = 0
58 | self.sharex = None
59 | self.figs = list()
60 | self.cursors = list()
61 | self.daxis = collections.OrderedDict()
62 | self.vaxis = list()
63 | self.zorder = dict()
64 | self.coloridx = collections.defaultdict(lambda: -1)
65 | self.handles = collections.defaultdict(list)
66 | self.labels = collections.defaultdict(list)
67 | self.legpos = collections.defaultdict(int)
68 |
69 | self.prop = mfontmgr.FontProperties(size=self.sch.subtxtsize)
70 |
71 | def newfig(self, figid, numfig, mpyplot):
72 | fig = mpyplot.figure(figid + numfig)
73 | self.figs.append(fig)
74 | self.daxis = collections.OrderedDict()
75 | self.vaxis = list()
76 | self.row = 0
77 | self.sharex = None
78 | return fig
79 |
80 | def nextcolor(self, ax):
81 | self.coloridx[ax] += 1
82 | return self.coloridx[ax]
83 |
84 | def color(self, ax):
85 | return self.sch.color(self.coloridx[ax])
86 |
87 | def zordernext(self, ax):
88 | z = self.zorder[ax]
89 | if self.sch.zdown:
90 | return z * 0.9999
91 | return z * 1.0001
92 |
93 | def zordercur(self, ax):
94 | return self.zorder[ax]
95 |
96 |
97 | class Plot_OldSync(with_metaclass(MetaParams, object)):
98 | params = (('scheme', PlotScheme()),)
99 |
100 | def __init__(self, **kwargs):
101 | for pname, pvalue in kwargs.items():
102 | setattr(self.p.scheme, pname, pvalue)
103 | if not hasattr(self.p.scheme, 'locbg'):
104 | setattr(self.p.scheme, 'locbg', 'white')
105 | setattr(self.p.scheme, 'locbgother', 'white')
106 |
107 | def drawtag(self, ax, x, y, facecolor, edgecolor, alpha=0.9, **kwargs):
108 |
109 | txt = ax.text(x, y, '%.2f' % y, va='center', ha='left',
110 | fontsize=self.pinf.sch.subtxtsize,
111 | bbox=dict(boxstyle=tag_box_style,
112 | facecolor=facecolor,
113 | edgecolor=edgecolor,
114 | alpha=alpha),
115 | # 3.0 is the minimum default for text
116 | zorder=self.pinf.zorder[ax] + 3.0,
117 | **kwargs)
118 |
119 | def plot(self, strategy, figid=0, numfigs=1, iplot=True,
120 | start=None, end=None, **kwargs):
121 | # pfillers={}):
122 | if not strategy.datas:
123 | return
124 |
125 | if not len(strategy):
126 | return
127 |
128 | if iplot:
129 | if 'ipykernel' in sys.modules:
130 | matplotlib.use('nbagg')
131 |
132 | # this import must not happen before matplotlib.use
133 | import matplotlib.pyplot as mpyplot
134 | self.mpyplot = mpyplot
135 |
136 | self.pinf = PInfo(self.p.scheme)
137 | self.sortdataindicators(strategy)
138 | self.calcrows(strategy)
139 |
140 | st_dtime = strategy.lines.datetime.plot()
141 | if start is None:
142 | start = 0
143 | if end is None:
144 | end = len(st_dtime)
145 |
146 | if isinstance(start, datetime.date):
147 | start = bisect.bisect_left(st_dtime, date2num(start))
148 |
149 | if isinstance(end, datetime.date):
150 | end = bisect.bisect_right(st_dtime, date2num(end))
151 |
152 | if end < 0:
153 | end = len(st_dtime) + 1 + end # -1 = len() -2 = len() - 1
154 |
155 | slen = len(st_dtime[start:end])
156 | d, m = divmod(slen, numfigs)
157 | pranges = list()
158 | for i in range(numfigs):
159 | a = d * i + start
160 | if i == (numfigs - 1):
161 | d += m # add remainder to last stint
162 | b = a + d
163 |
164 | pranges.append([a, b, d])
165 |
166 | figs = []
167 |
168 | for numfig in range(numfigs):
169 | # prepare a figure
170 | fig = self.pinf.newfig(figid, numfig, self.mpyplot)
171 | figs.append(fig)
172 |
173 | self.pinf.pstart, self.pinf.pend, self.pinf.psize = pranges[numfig]
174 | self.pinf.xstart = self.pinf.pstart
175 | self.pinf.xend = self.pinf.pend
176 |
177 | self.pinf.clock = strategy
178 | self.pinf.xreal = self.pinf.clock.datetime.plot(
179 | self.pinf.pstart, self.pinf.psize)
180 | self.pinf.xlen = len(self.pinf.xreal)
181 | self.pinf.x = list(range(self.pinf.xlen))
182 | # self.pinf.pfillers = {None: []}
183 | # for key, val in pfillers.items():
184 | # pfstart = bisect.bisect_left(val, self.pinf.pstart)
185 | # pfend = bisect.bisect_right(val, self.pinf.pend)
186 | # self.pinf.pfillers[key] = val[pfstart:pfend]
187 |
188 | # Do the plotting
189 | # Things that go always at the top (observers)
190 | self.pinf.xdata = self.pinf.x
191 | for ptop in self.dplotstop:
192 | self.plotind(None, ptop, subinds=self.dplotsover[ptop])
193 |
194 | # Create the rest on a per data basis
195 | dt0, dt1 = self.pinf.xreal[0], self.pinf.xreal[-1]
196 | for data in strategy.datas:
197 | if not data.plotinfo.plot:
198 | continue
199 |
200 | self.pinf.xdata = self.pinf.x
201 | xd = data.datetime.plotrange(self.pinf.xstart, self.pinf.xend)
202 | if len(xd) < self.pinf.xlen:
203 | self.pinf.xdata = xdata = []
204 | xreal = self.pinf.xreal
205 | dts = data.datetime.plot()
206 | xtemp = list()
207 | for dt in (x for x in dts if dt0 <= x <= dt1):
208 | dtidx = bisect.bisect_left(xreal, dt)
209 | xdata.append(dtidx)
210 | xtemp.append(dt)
211 |
212 | self.pinf.xstart = bisect.bisect_left(dts, xtemp[0])
213 | self.pinf.xend = bisect.bisect_right(dts, xtemp[-1])
214 |
215 | for ind in self.dplotsup[data]:
216 | self.plotind(
217 | data,
218 | ind,
219 | subinds=self.dplotsover[ind],
220 | upinds=self.dplotsup[ind],
221 | downinds=self.dplotsdown[ind])
222 |
223 | self.plotdata(data, self.dplotsover[data])
224 |
225 | for ind in self.dplotsdown[data]:
226 | self.plotind(
227 | data,
228 | ind,
229 | subinds=self.dplotsover[ind],
230 | upinds=self.dplotsup[ind],
231 | downinds=self.dplotsdown[ind])
232 |
233 | cursor = MultiCursor(
234 | fig.canvas, list(self.pinf.daxis.values()),
235 | useblit=True,
236 | horizOn=True, vertOn=True,
237 | horizMulti=False, vertMulti=True,
238 | horizShared=True, vertShared=False,
239 | color='black', lw=1, ls=':')
240 |
241 | self.pinf.cursors.append(cursor)
242 |
243 | # Put the subplots as indicated by hspace
244 | fig.subplots_adjust(hspace=self.pinf.sch.plotdist,
245 | top=0.98, left=0.05, bottom=0.05, right=0.95)
246 |
247 | laxis = list(self.pinf.daxis.values())
248 |
249 | # Find last axis which is not a twinx (date locator fails there)
250 | i = -1
251 | while True:
252 | lastax = laxis[i]
253 | if lastax not in self.pinf.vaxis:
254 | break
255 |
256 | i -= 1
257 |
258 | self.setlocators(lastax) # place the locators/fmts
259 |
260 | # Applying fig.autofmt_xdate if the data axis is the last one
261 | # breaks the presentation of the date labels. why?
262 | # Applying the manual rotation with setp cures the problem
263 | # but the labels from all axis but the last have to be hidden
264 | for ax in laxis:
265 | self.mpyplot.setp(ax.get_xticklabels(), visible=True)
266 |
267 | self.mpyplot.setp(lastax.get_xticklabels(), visible=True,
268 | rotation=self.pinf.sch.tickrotation)
269 |
270 | # Things must be tight along the x axis (to fill both ends)
271 | axtight = 'x' if not self.pinf.sch.ytight else 'both'
272 | self.mpyplot.autoscale(enable=True, axis=axtight, tight=True)
273 |
274 | return figs
275 |
276 | def setlocators(self, ax):
277 | clock = sorted(self.pinf.clock.datas,
278 | key=lambda x: (x._timeframe, x._compression))[0]
279 |
280 | comp = getattr(clock, '_compression', 1)
281 | tframe = getattr(clock, '_timeframe', TimeFrame.Days)
282 |
283 | if self.pinf.sch.fmt_x_data is None:
284 | if tframe == TimeFrame.Years:
285 | fmtdata = '%Y'
286 | elif tframe == TimeFrame.Months:
287 | fmtdata = '%Y-%m'
288 | elif tframe == TimeFrame.Weeks:
289 | fmtdata = '%Y-%m-%d'
290 | elif tframe == TimeFrame.Days:
291 | fmtdata = '%Y-%m-%d'
292 | elif tframe == TimeFrame.Minutes:
293 | fmtdata = '%Y-%m-%d %H:%M'
294 | elif tframe == TimeFrame.Seconds:
295 | fmtdata = '%Y-%m-%d %H:%M:%S'
296 | elif tframe == TimeFrame.MicroSeconds:
297 | fmtdata = '%Y-%m-%d %H:%M:%S.%f'
298 | elif tframe == TimeFrame.Ticks:
299 | fmtdata = '%Y-%m-%d %H:%M:%S.%f'
300 | else:
301 | fmtdata = self.pinf.sch.fmt_x_data
302 |
303 | fordata = MyDateFormatter(self.pinf.xreal, fmt=fmtdata)
304 | for dax in self.pinf.daxis.values():
305 | dax.fmt_xdata = fordata
306 |
307 | # Major locator / formatter
308 | locmajor = loc.AutoDateLocator(self.pinf.xreal)
309 | ax.xaxis.set_major_locator(locmajor)
310 | if self.pinf.sch.fmt_x_ticks is None:
311 | autofmt = loc.AutoDateFormatter(self.pinf.xreal, locmajor)
312 | else:
313 | autofmt = MyDateFormatter(self.pinf.xreal,
314 | fmt=self.pinf.sch.fmt_x_ticks)
315 | ax.xaxis.set_major_formatter(autofmt)
316 |
317 | def calcrows(self, strategy):
318 | # Calculate the total number of rows
319 | rowsmajor = self.pinf.sch.rowsmajor
320 | rowsminor = self.pinf.sch.rowsminor
321 | nrows = 0
322 |
323 | datasnoplot = 0
324 | for data in strategy.datas:
325 | if not data.plotinfo.plot:
326 | # neither data nor indicators nor volume add rows
327 | datasnoplot += 1
328 | self.dplotsup.pop(data, None)
329 | self.dplotsdown.pop(data, None)
330 | self.dplotsover.pop(data, None)
331 |
332 | else:
333 | pmaster = data.plotinfo.plotmaster
334 | if pmaster is data:
335 | pmaster = None
336 | if pmaster is not None:
337 | # data doesn't add a row, but volume may
338 | if self.pinf.sch.volume:
339 | nrows += rowsminor
340 | else:
341 | # data adds rows, volume may
342 | nrows += rowsmajor
343 | if self.pinf.sch.volume and not self.pinf.sch.voloverlay:
344 | nrows += rowsminor
345 |
346 | if False:
347 | # Datas and volumes
348 | nrows += (len(strategy.datas) - datasnoplot) * rowsmajor
349 | if self.pinf.sch.volume and not self.pinf.sch.voloverlay:
350 | nrows += (len(strategy.datas) - datasnoplot) * rowsminor
351 |
352 | # top indicators/observers
353 | nrows += len(self.dplotstop) * rowsminor
354 |
355 | # indicators above datas
356 | nrows += sum(len(v) for v in self.dplotsup.values())
357 | nrows += sum(len(v) for v in self.dplotsdown.values())
358 |
359 | self.pinf.nrows = nrows
360 |
361 | def newaxis(self, obj, rowspan):
362 | ax = self.mpyplot.subplot2grid(
363 | (self.pinf.nrows, 1), (self.pinf.row, 0),
364 | rowspan=rowspan, sharex=self.pinf.sharex)
365 |
366 | # update the sharex information if not available
367 | if self.pinf.sharex is None:
368 | self.pinf.sharex = ax
369 |
370 | # update the row index with the taken rows
371 | self.pinf.row += rowspan
372 |
373 | # save the mapping indicator - axis and return
374 | self.pinf.daxis[obj] = ax
375 |
376 | # Activate grid in all axes if requested
377 | ax.yaxis.tick_right()
378 | ax.grid(self.pinf.sch.grid, which='both')
379 |
380 | return ax
381 |
382 | def plotind(self, iref, ind,
383 | subinds=None, upinds=None, downinds=None,
384 | masterax=None):
385 |
386 | sch = self.p.scheme
387 |
388 | # check subind
389 | subinds = subinds or []
390 | upinds = upinds or []
391 | downinds = downinds or []
392 |
393 | # plot subindicators on self with independent axis above
394 | for upind in upinds:
395 | self.plotind(iref, upind)
396 |
397 | # Get an axis for this plot
398 | ax = masterax or self.newaxis(ind, rowspan=self.pinf.sch.rowsminor)
399 |
400 | indlabel = ind.plotlabel()
401 |
402 | # Scan lines quickly to find out if some lines have to be skipped for
403 | # legend (because matplotlib reorders the legend)
404 | toskip = 0
405 | for lineidx in range(ind.size()):
406 | line = ind.lines[lineidx]
407 | linealias = ind.lines._getlinealias(lineidx)
408 | lineplotinfo = getattr(ind.plotlines, '_%d' % lineidx, None)
409 | if not lineplotinfo:
410 | lineplotinfo = getattr(ind.plotlines, linealias, None)
411 | if not lineplotinfo:
412 | lineplotinfo = AutoInfoClass()
413 | pltmethod = lineplotinfo._get('_method', 'plot')
414 | if pltmethod != 'plot':
415 | toskip += 1 - lineplotinfo._get('_plotskip', False)
416 |
417 | if toskip >= ind.size():
418 | toskip = 0
419 |
420 | for lineidx in range(ind.size()):
421 | line = ind.lines[lineidx]
422 | linealias = ind.lines._getlinealias(lineidx)
423 |
424 | lineplotinfo = getattr(ind.plotlines, '_%d' % lineidx, None)
425 | if not lineplotinfo:
426 | lineplotinfo = getattr(ind.plotlines, linealias, None)
427 |
428 | if not lineplotinfo:
429 | lineplotinfo = AutoInfoClass()
430 |
431 | if lineplotinfo._get('_plotskip', False):
432 | continue
433 |
434 | # Legend label only when plotting 1st line
435 | if masterax and not ind.plotinfo.plotlinelabels:
436 | label = indlabel * (not toskip) or '_nolegend'
437 | else:
438 | label = (indlabel + '\n') * (not toskip)
439 | label += lineplotinfo._get('_name', '') or linealias
440 |
441 | toskip -= 1 # one line less until legend can be added
442 |
443 | # plot data
444 | lplot = line.plotrange(self.pinf.xstart, self.pinf.xend)
445 |
446 | # Global and generic for indicator
447 | if self.pinf.sch.linevalues and ind.plotinfo.plotlinevalues:
448 | plotlinevalue = lineplotinfo._get('_plotvalue', True)
449 | if plotlinevalue and not math.isnan(lplot[-1]):
450 | label += ' %.2f' % lplot[-1]
451 |
452 | plotkwargs = dict()
453 | linekwargs = lineplotinfo._getkwargs(skip_=True)
454 |
455 | if linekwargs.get('color', None) is None:
456 | if not lineplotinfo._get('_samecolor', False):
457 | self.pinf.nextcolor(ax)
458 | plotkwargs['color'] = self.pinf.color(ax)
459 |
460 | plotkwargs.update(dict(aa=True, label=label))
461 | plotkwargs.update(**linekwargs)
462 |
463 | if ax in self.pinf.zorder:
464 | plotkwargs['zorder'] = self.pinf.zordernext(ax)
465 |
466 | pltmethod = getattr(ax, lineplotinfo._get('_method', 'plot'))
467 |
468 | xdata, lplotarray = self.pinf.xdata, lplot
469 | if lineplotinfo._get('_skipnan', False):
470 | # Get the full array and a mask to skipnan
471 | lplotarray = np.array(lplot)
472 | lplotmask = np.isfinite(lplotarray)
473 |
474 | # Get both the axis and the data masked
475 | lplotarray = lplotarray[lplotmask]
476 | xdata = np.array(xdata)[lplotmask]
477 |
478 | plottedline = pltmethod(xdata, lplotarray, **plotkwargs)
479 | try:
480 | plottedline = plottedline[0]
481 | except:
482 | # Possibly a container of artists (when plotting bars)
483 | pass
484 |
485 | self.pinf.zorder[ax] = plottedline.get_zorder()
486 |
487 | vtags = lineplotinfo._get('plotvaluetags', True)
488 | if self.pinf.sch.valuetags and vtags:
489 | linetag = lineplotinfo._get('_plotvaluetag', True)
490 | if linetag and not math.isnan(lplot[-1]):
491 | # line has valid values, plot a tag for the last value
492 | self.drawtag(ax, len(self.pinf.xreal), lplot[-1],
493 | facecolor=self.pinf.sch.locbgother,
494 | edgecolor=self.pinf.color(ax))
495 |
496 | farts = (('_gt', operator.gt), ('_lt', operator.lt), ('', None),)
497 | for fcmp, fop in farts:
498 | fattr = '_fill' + fcmp
499 | fref, fcol = lineplotinfo._get(fattr, (None, None))
500 | if fref is not None:
501 | y1 = np.array(lplot)
502 | if isinstance(fref, integer_types):
503 | y2 = np.full_like(y1, fref)
504 | else: # string, naming a line, nothing else is supported
505 | l2 = getattr(ind, fref)
506 | prl2 = l2.plotrange(self.pinf.xstart, self.pinf.xend)
507 | y2 = np.array(prl2)
508 | kwargs = dict()
509 | if fop is not None:
510 | kwargs['where'] = fop(y1, y2)
511 |
512 | falpha = self.pinf.sch.fillalpha
513 | if isinstance(fcol, (list, tuple)):
514 | fcol, falpha = fcol
515 |
516 | ax.fill_between(self.pinf.xdata, y1, y2,
517 | facecolor=fcol,
518 | alpha=falpha,
519 | interpolate=True,
520 | **kwargs)
521 |
522 | # plot subindicators that were created on self
523 | for subind in subinds:
524 | self.plotind(iref, subind, subinds=self.dplotsover[subind],
525 | masterax=ax)
526 |
527 | if not masterax:
528 | # adjust margin if requested ... general of particular
529 | ymargin = ind.plotinfo._get('plotymargin', 0.0)
530 | ymargin = max(ymargin, self.pinf.sch.yadjust)
531 | if ymargin:
532 | ax.margins(y=ymargin)
533 |
534 | # Set specific or generic ticks
535 | yticks = ind.plotinfo._get('plotyticks', [])
536 | if not yticks:
537 | yticks = ind.plotinfo._get('plotyhlines', [])
538 |
539 | if yticks:
540 | ax.set_yticks(yticks)
541 | else:
542 | locator = mticker.MaxNLocator(nbins=4, prune='both')
543 | ax.yaxis.set_major_locator(locator)
544 |
545 | # Set specific hlines if asked to
546 | hlines = ind.plotinfo._get('plothlines', [])
547 | if not hlines:
548 | hlines = ind.plotinfo._get('plotyhlines', [])
549 | for hline in hlines:
550 | ax.axhline(hline, color=self.pinf.sch.hlinescolor,
551 | ls=self.pinf.sch.hlinesstyle,
552 | lw=self.pinf.sch.hlineswidth)
553 |
554 | if self.pinf.sch.legendind and \
555 | ind.plotinfo._get('plotlegend', True):
556 |
557 | handles, labels = ax.get_legend_handles_labels()
558 | # Ensure that we have something to show
559 | if labels:
560 | # location can come from the user
561 | loc = ind.plotinfo.legendloc or self.pinf.sch.legendindloc
562 |
563 | # Legend done here to ensure it includes all plots
564 | legend = ax.legend(loc=loc,
565 | numpoints=1, frameon=False,
566 | shadow=False, fancybox=False,
567 | prop=self.pinf.prop)
568 |
569 | # legend.set_title(indlabel, prop=self.pinf.prop)
570 | # hack: if title is set. legend has a Vbox for the labels
571 | # which has a default "center" set
572 | legend._legend_box.align = 'left'
573 |
574 | # plot subindicators on self with independent axis below
575 | for downind in downinds:
576 | self.plotind(iref, downind)
577 |
578 | def plotvolume(self, data, opens, highs, lows, closes, volumes, label):
579 | pmaster = data.plotinfo.plotmaster
580 | if pmaster is data:
581 | pmaster = None
582 | voloverlay = (self.pinf.sch.voloverlay and pmaster is None)
583 |
584 | # if sefl.pinf.sch.voloverlay:
585 | if voloverlay:
586 | rowspan = self.pinf.sch.rowsmajor
587 | else:
588 | rowspan = self.pinf.sch.rowsminor
589 |
590 | ax = self.newaxis(data.volume, rowspan=rowspan)
591 |
592 | # if self.pinf.sch.voloverlay:
593 | if voloverlay:
594 | volalpha = self.pinf.sch.voltrans
595 | else:
596 | volalpha = 1.0
597 |
598 | maxvol = volylim = max(volumes)
599 | if maxvol:
600 |
601 | # Plot the volume (no matter if as overlay or standalone)
602 | vollabel = label
603 | volplot, = plot_volume(ax, self.pinf.xdata, opens, closes, volumes,
604 | colorup=self.pinf.sch.volup,
605 | colordown=self.pinf.sch.voldown,
606 | alpha=volalpha, label=vollabel)
607 |
608 | nbins = 6
609 | prune = 'both'
610 | # if self.pinf.sch.voloverlay:
611 | if voloverlay:
612 | # store for a potential plot over it
613 | nbins = int(nbins / self.pinf.sch.volscaling)
614 | prune = None
615 |
616 | volylim /= self.pinf.sch.volscaling
617 | ax.set_ylim(0, volylim, auto=True)
618 | else:
619 | # plot a legend
620 | handles, labels = ax.get_legend_handles_labels()
621 | if handles:
622 |
623 | # location can come from the user
624 | loc = data.plotinfo.legendloc or self.pinf.sch.legendindloc
625 |
626 | # Legend done here to ensure it includes all plots
627 | legend = ax.legend(loc=loc,
628 | numpoints=1, frameon=False,
629 | shadow=False, fancybox=False,
630 | prop=self.pinf.prop)
631 |
632 | locator = mticker.MaxNLocator(nbins=nbins, prune=prune)
633 | ax.yaxis.set_major_locator(locator)
634 | ax.yaxis.set_major_formatter(MyVolFormatter(maxvol))
635 |
636 | if not maxvol:
637 | ax.set_yticks([])
638 | return None
639 |
640 | return volplot
641 |
642 | def plotdata(self, data, indicators):
643 | for ind in indicators:
644 | upinds = self.dplotsup[ind]
645 | for upind in upinds:
646 | self.plotind(data, upind,
647 | subinds=self.dplotsover[upind],
648 | upinds=self.dplotsup[upind],
649 | downinds=self.dplotsdown[upind])
650 |
651 | opens = data.open.plotrange(self.pinf.xstart, self.pinf.xend)
652 | highs = data.high.plotrange(self.pinf.xstart, self.pinf.xend)
653 | lows = data.low.plotrange(self.pinf.xstart, self.pinf.xend)
654 | closes = data.close.plotrange(self.pinf.xstart, self.pinf.xend)
655 | volumes = data.volume.plotrange(self.pinf.xstart, self.pinf.xend)
656 |
657 | vollabel = 'Volume'
658 | pmaster = data.plotinfo.plotmaster
659 | if pmaster is data:
660 | pmaster = None
661 |
662 | datalabel = ''
663 | if hasattr(data, '_name') and data._name:
664 | datalabel += data._name
665 |
666 | voloverlay = (self.pinf.sch.voloverlay and pmaster is None)
667 |
668 | if not voloverlay:
669 | vollabel += ' ({})'.format(datalabel)
670 |
671 | # if self.pinf.sch.volume and self.pinf.sch.voloverlay:
672 | axdatamaster = None
673 | if self.pinf.sch.volume and voloverlay:
674 | volplot = self.plotvolume(
675 | data, opens, highs, lows, closes, volumes, vollabel)
676 | axvol = self.pinf.daxis[data.volume]
677 | ax = axvol.twinx()
678 | self.pinf.daxis[data] = ax
679 | self.pinf.vaxis.append(ax)
680 | else:
681 | if pmaster is None:
682 | ax = self.newaxis(data, rowspan=self.pinf.sch.rowsmajor)
683 | elif getattr(data.plotinfo, 'sameaxis', False):
684 | axdatamaster = self.pinf.daxis[pmaster]
685 | ax = axdatamaster
686 | else:
687 | axdatamaster = self.pinf.daxis[pmaster]
688 | ax = axdatamaster.twinx()
689 | self.pinf.vaxis.append(ax)
690 |
691 | if hasattr(data, '_compression') and \
692 | hasattr(data, '_timeframe'):
693 | tfname = TimeFrame.getname(data._timeframe, data._compression)
694 | datalabel += ' (%d %s)' % (data._compression, tfname)
695 |
696 | plinevalues = getattr(data.plotinfo, 'plotlinevalues', True)
697 | if self.pinf.sch.style.startswith('line'):
698 | if self.pinf.sch.linevalues and plinevalues:
699 | datalabel += ' C:%.2f' % closes[-1]
700 |
701 | if axdatamaster is None:
702 | color = self.pinf.sch.loc
703 | else:
704 | self.pinf.nextcolor(axdatamaster)
705 | color = self.pinf.color(axdatamaster)
706 |
707 | plotted = plot_lineonclose(
708 | ax, self.pinf.xdata, closes,
709 | color=color, label=datalabel)
710 | else:
711 | if self.pinf.sch.linevalues and plinevalues:
712 | datalabel += ' O:%.2f H:%.2f L:%.2f C:%.2f' % \
713 | (opens[-1], highs[-1], lows[-1], closes[-1])
714 | if self.pinf.sch.style.startswith('candle'):
715 | plotted = plot_candlestick(
716 | ax, self.pinf.xdata, opens, highs, lows, closes,
717 | colorup=self.pinf.sch.barup,
718 | colordown=self.pinf.sch.bardown,
719 | label=datalabel,
720 | alpha=self.pinf.sch.baralpha,
721 | fillup=self.pinf.sch.barupfill,
722 | filldown=self.pinf.sch.bardownfill)
723 |
724 | elif self.pinf.sch.style.startswith('bar') or True:
725 | # final default option -- should be "else"
726 | plotted = plot_ohlc(
727 | ax, self.pinf.xdata, opens, highs, lows, closes,
728 | colorup=self.pinf.sch.barup,
729 | colordown=self.pinf.sch.bardown,
730 | label=datalabel)
731 |
732 | self.pinf.zorder[ax] = plotted[0].get_zorder()
733 |
734 | # Code to place a label at the right hand side with the last value
735 | vtags = data.plotinfo._get('plotvaluetags', True)
736 | if self.pinf.sch.valuetags and vtags:
737 | self.drawtag(ax, len(self.pinf.xreal), closes[-1],
738 | facecolor=self.pinf.sch.locbg,
739 | edgecolor=self.pinf.sch.loc)
740 |
741 | ax.yaxis.set_major_locator(mticker.MaxNLocator(prune='both'))
742 | # make sure "over" indicators do not change our scale
743 | if data.plotinfo._get('plotylimited', True):
744 | if axdatamaster is None:
745 | ax.set_ylim(ax.get_ylim())
746 |
747 | if self.pinf.sch.volume:
748 | # if not self.pinf.sch.voloverlay:
749 | if not voloverlay:
750 | self.plotvolume(
751 | data, opens, highs, lows, closes, volumes, vollabel)
752 | else:
753 | # Prepare overlay scaling/pushup or manage own axis
754 | if self.pinf.sch.volpushup:
755 | # push up overlaid axis by lowering the bottom limit
756 | axbot, axtop = ax.get_ylim()
757 | axbot *= (1.0 - self.pinf.sch.volpushup)
758 | ax.set_ylim(axbot, axtop)
759 |
760 | for ind in indicators:
761 | self.plotind(data, ind, subinds=self.dplotsover[ind], masterax=ax)
762 |
763 | handles, labels = ax.get_legend_handles_labels()
764 | a = axdatamaster or ax
765 | if handles:
766 | # put data and volume legend entries in the 1st positions
767 | # because they are "collections" they are considered after Line2D
768 | # for the legend entries, which is not our desire
769 | # if self.pinf.sch.volume and self.pinf.sch.voloverlay:
770 |
771 | ai = self.pinf.legpos[a]
772 | if self.pinf.sch.volume and voloverlay:
773 | if volplot:
774 | # even if volume plot was requested, there may be no volume
775 | labels.insert(ai, vollabel)
776 | handles.insert(ai, volplot)
777 |
778 | didx = labels.index(datalabel)
779 | labels.insert(ai, labels.pop(didx))
780 | handles.insert(ai, handles.pop(didx))
781 |
782 | if axdatamaster is None:
783 | self.pinf.handles[ax] = handles
784 | self.pinf.labels[ax] = labels
785 | else:
786 | self.pinf.handles[axdatamaster] = handles
787 | self.pinf.labels[axdatamaster] = labels
788 | # self.pinf.handles[axdatamaster].extend(handles)
789 | # self.pinf.labels[axdatamaster].extend(labels)
790 |
791 | h = self.pinf.handles[a]
792 | l = self.pinf.labels[a]
793 |
794 | axlegend = a
795 | loc = data.plotinfo.legendloc or self.pinf.sch.legenddataloc
796 | legend = axlegend.legend(h, l,
797 | loc=loc,
798 | frameon=False, shadow=False,
799 | fancybox=False, prop=self.pinf.prop,
800 | numpoints=1, ncol=1)
801 |
802 | # hack: if title is set. legend has a Vbox for the labels
803 | # which has a default "center" set
804 | legend._legend_box.align = 'left'
805 |
806 | for ind in indicators:
807 | downinds = self.dplotsdown[ind]
808 | for downind in downinds:
809 | self.plotind(data, downind,
810 | subinds=self.dplotsover[downind],
811 | upinds=self.dplotsup[downind],
812 | downinds=self.dplotsdown[downind])
813 |
814 | self.pinf.legpos[a] = len(self.pinf.handles[a])
815 |
816 | if data.plotinfo._get('plotlog', False):
817 | a = axdatamaster or ax
818 | a.set_yscale('log')
819 |
820 | def show(self):
821 | self.mpyplot.show()
822 |
823 | def savefig(self, fig, filename, width=16, height=9, dpi=300, tight=True):
824 | fig.set_size_inches(width, height)
825 | bbox_inches = 'tight' * tight or None
826 | fig.savefig(filename, dpi=dpi, bbox_inches=bbox_inches)
827 |
828 | def sortdataindicators(self, strategy):
829 | # These lists/dictionaries hold the subplots that go above each data
830 | self.dplotstop = list()
831 | self.dplotsup = collections.defaultdict(list)
832 | self.dplotsdown = collections.defaultdict(list)
833 | self.dplotsover = collections.defaultdict(list)
834 |
835 | # Sort observers in the different lists/dictionaries
836 | for x in strategy.getobservers():
837 | if not x.plotinfo.plot or x.plotinfo.plotskip:
838 | continue
839 |
840 | if x.plotinfo.subplot:
841 | self.dplotstop.append(x)
842 | else:
843 | key = getattr(x._clock, 'owner', x._clock)
844 | self.dplotsover[key].append(x)
845 |
846 | # Sort indicators in the different lists/dictionaries
847 | for x in strategy.getindicators():
848 | if not hasattr(x, 'plotinfo'):
849 | # no plotting support - so far LineSingle derived classes
850 | continue
851 |
852 | if not x.plotinfo.plot or x.plotinfo.plotskip:
853 | continue
854 |
855 | x._plotinit() # will be plotted ... call its init function
856 |
857 | # support LineSeriesStub which has "owner" to point to the data
858 | key = getattr(x._clock, 'owner', x._clock)
859 | if key is strategy: # a LinesCoupler
860 | key = strategy.data
861 |
862 | if getattr(x.plotinfo, 'plotforce', False):
863 | if key not in strategy.datas:
864 | datas = strategy.datas
865 | while True:
866 | if key not in strategy.datas:
867 | key = key._clock
868 | else:
869 | break
870 |
871 | xpmaster = x.plotinfo.plotmaster
872 | if xpmaster is x:
873 | xpmaster = None
874 | if xpmaster is not None:
875 | key = xpmaster
876 |
877 | if x.plotinfo.subplot and xpmaster is None:
878 | if x.plotinfo.plotabove:
879 | self.dplotsup[key].append(x)
880 | else:
881 | self.dplotsdown[key].append(x)
882 | else:
883 | self.dplotsover[key].append(x)
884 |
885 |
886 | Plot = Plot_OldSync
887 |
--------------------------------------------------------------------------------