增加vectorbt源码

This commit is contained in:
2025-11-01 09:32:26 +08:00
parent 63b2c3c06a
commit bbfda1446c
139 changed files with 176161 additions and 741 deletions

View File

@ -0,0 +1,26 @@
FROM python:3.8-slim
RUN apt-get -y update && apt-get -y install gcc curl make
RUN pip install --upgrade pip
# Required by TA-Lib and numba
RUN pip install numpy>=1.19.4
RUN curl -O https://netcologne.dl.sourceforge.net/project/ta-lib/ta-lib/0.4.0/ta-lib-0.4.0-src.tar.gz \
&& tar -xzf ta-lib-0.4.0-src.tar.gz \
&& cd ta-lib/ \
&& ./configure --prefix=/usr \
&& make \
&& make install \
&& cd ..
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Clean up APT when done.
RUN apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
COPY assets ./assets
COPY app.py .
CMD ["python", "app.py"]

View File

@ -0,0 +1 @@
web: gunicorn app:server

View File

@ -0,0 +1,72 @@
# candlestick-patterns
This app creates a dashboard using [Dash](https://github.com/plotly/dash) to visualize and backtest candlestick patterns.
* Supports [Yahoo! Finance](https://github.com/ranaroussi/yfinance) tickers
* Supports [TA-Lib](https://github.com/mrjbq7/ta-lib) candlestick patterns
* Allows to choose entry and exit patterns, and override candle settings
* Allows to specify signals manually
* Performs backtesting on selected signals using [vectorbt](https://github.com/polakowo/vectorbt)
* Visualizes OHLCV, signals, orders, trades and portfolio value using [Plotly](https://github.com/plotly/plotly.py)
* Displays key performance metrics such as Sharpe ratio
* Compares main strategy to holding and trading randomly
* Responsive design using [Dash Bootstrap Components](https://github.com/facultyai/dash-bootstrap-components)
## How to run the app
### Using Docker
Build the Docker image and run the container:
```bash
docker build -t candlestick-patterns .
docker run -p 8050:8050 -e HOST='0.0.0.0' candlestick-patterns
```
Visit [http://127.0.0.1:8050/](http://127.0.0.1:8050/)
Note: Compiling for the first time may take a while.
### Using virtual environment
To get started, first clone this repo:
```bash
git clone https://github.com/polakowo/vectorbt.git
cd vectorbt/apps/candlestick-patterns
```
Create and activate a conda env:
```bash
conda create -n candlestick-patterns python=3.7.6
conda activate candlestick-patterns
```
Or a venv (make sure your Python is 3.6+):
```bash
python3 -m venv venv
source venv/bin/activate # Unix
venv\Scripts\activate # Windows
```
Install the requirements:
```bash
pip install -r requirements.txt
```
In case of errors related to TA-Lib, see [Troubleshooting](https://github.com/mrjbq7/ta-lib#troubleshooting).
Run the app:
```bash
python app.py
```
Visit [http://127.0.0.1:8050/](http://127.0.0.1:8050/)
## Screenshot
![screenshot.png](https://raw.githubusercontent.com/polakowo/vectorbt/master/apps/candlestick-patterns/screenshot.png)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,614 @@
/* Table of contents
- Plotly.js
- Grid
- Base Styles
- Typography
- Links
- Buttons
- Forms
- Lists
- Code
- Tables
- Spacing
- Utilities
- Clearing
- Media Queries
*/
/* PLotly.js
*/
/* plotly.js's modebar's z-index is 1001 by default
* https://github.com/plotly/plotly.js/blob/7e4d8ab164258f6bd48be56589dacd9bdd7fded2/src/css/_modebar.scss#L5
* In case a dropdown is above the graph, the dropdown's options
* will be rendered below the modebar
* Increase the select option's z-index
*/
/* This was actually not quite right -
dropdowns were overlapping each other (edited October 26)
.Select {
z-index: 1002;
}*/
/* Grid
*/
.container {
position: relative;
width: 100%;
max-width: 960px;
margin: 0 auto;
padding: 0 20px;
box-sizing: border-box;
}
.column,
.columns {
width: 100%;
float: left;
box-sizing: border-box;
}
/* For devices larger than 400px */
@media (min-width: 400px) {
.container {
width: 85%;
padding: 0;
}
}
/* For devices larger than 550px */
@media (min-width: 550px) {
.container {
width: 80%;
}
.column,
.columns {
margin-left: 2%;
}
.column:first-child,
.columns:first-child {
margin-left: 2%;
}
.one.column,
.one.columns {
width: 4.66666666667%;
}
.two.columns {
width: 13.3333333333%;
}
.three.columns {
width: 22%;
}
.four.columns {
width: 30.6666666667%;
}
.five.columns {
width: 39.3333333333%;
}
.six.columns {
width: 48%;
}
.seven.columns {
width: 56.6666666667%;
}
.eight.columns {
width: 65.3333333333%;
}
.nine.columns {
width: 74.0%;
}
.ten.columns {
width: 82.6666666667%;
}
.eleven.columns {
width: 91.3333333333%;
}
.twelve.columns {
width: 100%;
margin-left: 0;
}
.one-third.column {
width: 30.6666666667%;
}
.two-thirds.column {
width: 65.3333333333%;
}
.one-half.column {
width: 48%;
}
/* Offsets */
.offset-by-one.column,
.offset-by-one.columns {
margin-left: 8.66666666667%;
}
.offset-by-two.column,
.offset-by-two.columns {
margin-left: 17.3333333333%;
}
.offset-by-three.column,
.offset-by-three.columns {
margin-left: 26%;
}
.offset-by-four.column,
.offset-by-four.columns {
margin-left: 34.6666666667%;
}
.offset-by-five.column,
.offset-by-five.columns {
margin-left: 43.3333333333%;
}
.offset-by-six.column,
.offset-by-six.columns {
margin-left: 52%;
}
.offset-by-seven.column,
.offset-by-seven.columns {
margin-left: 60.6666666667%;
}
.offset-by-eight.column,
.offset-by-eight.columns {
margin-left: 69.3333333333%;
}
.offset-by-nine.column,
.offset-by-nine.columns {
margin-left: 78.0%;
}
.offset-by-ten.column,
.offset-by-ten.columns {
margin-left: 86.6666666667%;
}
.offset-by-eleven.column,
.offset-by-eleven.columns {
margin-left: 95.3333333333%;
}
.offset-by-one-third.column,
.offset-by-one-third.columns {
margin-left: 34.6666666667%;
}
.offset-by-two-thirds.column,
.offset-by-two-thirds.columns {
margin-left: 69.3333333333%;
}
.offset-by-one-half.column,
.offset-by-one-half.columns {
margin-left: 52%;
}
}
/* Base Styles
*/
/* NOTE
html is set to 62.5% so that all the REM measurements throughout Skeleton
are based on 10px sizing. So basically 1.5rem = 15px :) */
html {
font-size: 62.5%;
}
body {
font-size: 1.5em;
/* currently ems cause chrome bug misinterpreting rems on body element */
line-height: 1.6;
font-weight: 400;
font-family: "Open Sans", "HelveticaNeue", "Helvetica Neue", Helvetica, Arial, sans-serif;
color: rgb(50, 50, 50);
}
/* Typography
*/
h1,
h2,
h3,
h4,
h5,
h6 {
margin-top: 0;
margin-bottom: 0;
font-weight: 300;
}
h1 {
font-size: 4.5rem;
line-height: 1.2;
letter-spacing: -.1rem;
margin-bottom: 2rem;
}
h2 {
font-size: 3.6rem;
line-height: 1.25;
letter-spacing: -.1rem;
margin-bottom: 1.8rem;
margin-top: 1.8rem;
}
h3 {
font-size: 3.0rem;
line-height: 1.3;
letter-spacing: -.1rem;
margin-bottom: 1.5rem;
margin-top: 1.5rem;
}
h4 {
font-size: 2.6rem;
line-height: 1.35;
letter-spacing: -.08rem;
margin-bottom: 1.2rem;
margin-top: 1.2rem;
}
h5 {
font-size: 2.2rem;
line-height: 1.5;
letter-spacing: -.05rem;
margin-bottom: 0.6rem;
margin-top: 0.6rem;
}
h6 {
font-size: 2.0rem;
line-height: 1.6;
letter-spacing: 0;
margin-bottom: 0.75rem;
margin-top: 0.75rem;
}
p {
margin-top: 0;
}
/* Blockquotes
*/
blockquote {
border-left: 4px lightgrey solid;
padding-left: 1rem;
margin-top: 2rem;
margin-bottom: 2rem;
margin-left: 0rem;
}
/* Links
*/
a {
color: #1EAEDB;
text-decoration: underline;
cursor: pointer;
}
a:hover {
color: #0FA0CE;
}
/* Buttons
*/
.button,
button,
input[type="submit"],
input[type="reset"],
input[type="button"] {
display: inline-block;
height: 38px;
padding: 0 30px;
color: #555;
text-align: center;
font-size: 11px;
font-weight: 600;
line-height: 38px;
letter-spacing: .1rem;
text-transform: uppercase;
text-decoration: none;
white-space: nowrap;
background-color: transparent;
border-radius: 4px;
border: 1px solid #bbb;
cursor: pointer;
box-sizing: border-box;
}
.button:hover,
button:hover,
input[type="submit"]:hover,
input[type="reset"]:hover,
input[type="button"]:hover,
.button:focus,
button:focus,
input[type="submit"]:focus,
input[type="reset"]:focus,
input[type="button"]:focus {
color: #333;
border-color: #888;
outline: 0;
}
.button.button-primary,
button.button-primary,
input[type="submit"].button-primary,
input[type="reset"].button-primary,
input[type="button"].button-primary {
color: #FFF;
background-color: #33C3F0;
border-color: #33C3F0;
}
.button.button-primary:hover,
button.button-primary:hover,
input[type="submit"].button-primary:hover,
input[type="reset"].button-primary:hover,
input[type="button"].button-primary:hover,
.button.button-primary:focus,
button.button-primary:focus,
input[type="submit"].button-primary:focus,
input[type="reset"].button-primary:focus,
input[type="button"].button-primary:focus {
color: #FFF;
background-color: #1EAEDB;
border-color: #1EAEDB;
}
/* Forms
*/
input[type="email"],
input[type="number"],
input[type="search"],
input[type="text"],
input[type="tel"],
input[type="url"],
input[type="password"],
textarea,
select {
height: 38px;
padding: 6px 10px;
/* The 6px vertically centers text on FF, ignored by Webkit */
background-color: #fff;
border: 1px solid #D1D1D1;
border-radius: 4px;
box-shadow: none;
box-sizing: border-box;
font-family: inherit;
font-size: inherit;
/*https://stackoverflow.com/questions/6080413/why-doesnt-input-inherit-the-font-from-body*/
}
/* Removes awkward default styles on some inputs for iOS */
input[type="email"],
input[type="number"],
input[type="search"],
input[type="text"],
input[type="tel"],
input[type="url"],
input[type="password"],
textarea {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
}
textarea {
min-height: 65px;
padding-top: 6px;
padding-bottom: 6px;
}
input[type="email"]:focus,
input[type="number"]:focus,
input[type="search"]:focus,
input[type="text"]:focus,
input[type="tel"]:focus,
input[type="url"]:focus,
input[type="password"]:focus,
textarea:focus,
/*select:focus {*/
/* border: 1px solid #33C3F0;*/
/* outline: 0; }*/
label,
legend {
display: block;
margin-bottom: 0px;
}
fieldset {
padding: 0;
border-width: 0;
}
input[type="checkbox"],
input[type="radio"] {
display: inline;
}
label>.label-body {
display: inline-block;
margin-left: .5rem;
font-weight: normal;
}
/* Lists
*/
ul {
list-style: circle inside;
}
ol {
list-style: decimal inside;
}
ol,
ul {
padding-left: 0;
margin-top: 0;
}
ul ul,
ul ol,
ol ol,
ol ul {
margin: 1.5rem 0 1.5rem 3rem;
font-size: 90%;
}
li {
margin-bottom: 1rem;
}
/* Tables
*/
table {
border-collapse: collapse;
}
th,
td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #E1E1E1;
}
th:first-child,
td:first-child {
padding-left: 0;
}
th:last-child,
td:last-child {
padding-right: 0;
}
/* Spacing
*/
button,
.button {
margin-bottom: 0rem;
}
input,
textarea,
select,
fieldset {
margin-bottom: 0rem;
}
pre,
dl,
figure,
table,
form {
margin-bottom: 0rem;
}
p,
ul,
ol {
margin-bottom: 0.75rem;
}
/* Utilities
*/
.u-full-width {
width: 100%;
box-sizing: border-box;
}
.u-max-full-width {
max-width: 100%;
box-sizing: border-box;
}
.u-pull-right {
float: right;
}
.u-pull-left {
float: left;
}
/* Misc
*/
hr {
margin-top: 3rem;
margin-bottom: 3.5rem;
border-width: 0;
border-top: 1px solid #E1E1E1;
}
/* Clearing
*/
/* Self Clearing Goodness */
.container:after,
.row:after,
.u-cf {
content: "";
display: table;
clear: both;
}
/* Media Queries
*/
/*
Note: The best way to structure the use of media queries is to create the queries
near the relevant code. For example, if you wanted to change the styles for buttons
on small devices, paste the mobile query code up in the buttons section and style it
there.
*/
/* Larger than mobile */
@media (min-width: 400px) {}
/* Larger than phablet (also point when grid becomes active) */
@media (min-width: 550px) {}
/* Larger than tablet */
@media (min-width: 750px) {}
/* Larger than desktop */
@media (min-width: 1000px) {}
/* Larger than Desktop HD */
@media (min-width: 1200px) {}

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

View File

@ -0,0 +1,202 @@
:root {
--bgcolor: #272a32;
--dark_bgcolor: #1d2026;
--fontcolor: #9fa6b7;
--dark_fontcolor: #7b7d8d;
--gridcolor: #323b56;
--loadcolor: #387c9e;
--active_color: #88ccee;
}
.container.scalable {
width: 100%;
max-width: none;
}
/* Remove Undo
*/
._dash-undo-redo {
display: none;
}
body {
background-color: var(--dark_bgcolor);
color: var(--fontcolor);
overflow-x: hidden;
padding: 15px 30px;
margin: 0px;
}
button, .button {
background-color: var(--dark_bgcolor) !important;
color: var(--dark_fontcolor) !important;
border: 1px solid dimgrey !important;
}
button:hover, .button:hover {
color: var(--active_color) !important;
border: 1px solid var(--active_color) !important;
}
.banner {
height: 5rem;
margin-bottom: 1rem;
display: flex;
flex-direction: row nowrap;
align-items: center;
justify-content: space-between;
}
.banner h6 {
font-family: 'Open Sans', sans-serif;
font-size: 2rem;
font-weight: 600;
line-height: 1;
text-transform: uppercase;
}
.control-row {
display: flex;
flex-direction: row;
justify-content: space-between;
margin-bottom: 10px;
}
.half-width {
width: 45%;
}
.full-width {
width: 100%;
}
*.Select-control,
Select--single,
.Select-multi-value-wrapper,
.Select-value,
.Select-value-label,
.Select-placeholder,
.Select-multi-value-wrapper,
.Select-input input,
.input-control {
background-color: var(--dark_bgcolor) !important;
color: var(--dark_fontcolor) !important;
}
.Select-control,
.input-control {
border: 1px solid dimgrey !important;
}
.Select.is-focused .Select-input input {
color: var(--active_color) !important;
}
.Select.is-focused .Select-value .Select-value-label {
color: var(--active_color) !important;
}
.Select-menu-outer,
.Select.is-focused .Select-control,
.input-control:focus {
color: var(--active_color) !important;
border: 1px solid var(--active_color) !important;
}
.input-control {
width: 100%;
height: 36px;
}
span.Select-clear-zone {
display: none !important;
}
.Select-menu-outer,
.Select-menu-outer div,
.Select--multi {
background-color: var(--dark_bgcolor) !important;
color: var(--dark_fontcolor) !important;
}
.Select-menu-outer div:hover {
color: var(--active_color) !important;
cursor: pointer;
}
label, small {
color: var(--dark_fontcolor);
}
.control-outer {
display: inline-block;
}
.section-title {
font-size: 16px;
font-weight: bold;
display: block;
margin-top: 20px;
margin-bottom: 5px;
cursor: pointer;
}
.section-title:focus {
outline: none;
}
.app__left__section {
min-height: 100vh;
max-height: 100vh;
}
.app__right__section {
min-height: 100vh;
max-height: 100vh;
}
.pretty-container {
margin: 15px 0px;
padding: 25px;
background-color: var(--bgcolor);
border-radius: 5px;
box-shadow: 0 10px 20px rgba(0,0,0,0.19), 0 6px 6px rgba(0,0,0,0.23);
}
#yfinance_checklist {
color: var(--dark_fontcolor);
}
.rc-slider {
margin-top: 10px;
}
.rc-slider-rail {
background-color: var(--dark_bgcolor) !important
}
.rc-slider-track {
background-color: var(--loadcolor) !important
}
.rc-slider-dot-active, .rc-slider-handle {
border-color: var(--active_color) !important;
background-color: var(--active_color);
}
.rc-slider-handle {
border: none !important;
}
.column-1 {
padding-right: 15px !important;
}
.dash-cell.column-2 .input-active,
.dash-cell.column-3 .input-active {
color: var(--active_color) !important;
}
.dash-spreadsheet-inner .row {
margin: 0px;
}

View File

@ -0,0 +1,11 @@
numpy>=1.21.0
pandas>=1.3.0
dash>=1.21.0
dash_bootstrap_components>=0.13.0
gunicorn>=20.1.0
Flask-Caching>=1.10.1
TA-Lib>=0.4.21
ipywidgets>=7.6.3
matplotlib>=3.4.2
vectorbt>=0.21.0
yfinance>=0.1.62

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB