Compare commits
79 Commits
vbtnewvers
...
feature/mu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30931f60d4 | ||
|
|
79a033a633 | ||
|
|
a10d5b8a64 | ||
|
|
30044dc4ea | ||
|
|
30048364bb | ||
|
|
badd5b87dd | ||
|
|
8e5a56a28c | ||
|
|
591a9643eb | ||
|
|
41b3c0d839 | ||
|
|
dff6680098 | ||
|
|
88dd8e84dd | ||
|
|
45f022c16b | ||
|
|
9fb6794997 | ||
|
|
20e38fe223 | ||
|
|
f2ab00559a | ||
|
|
fc10cf3907 | ||
|
|
2f15b0b2a7 | ||
|
|
0bda14409d | ||
|
|
d7bde54533 | ||
|
|
ad45b424f7 | ||
|
|
ee5c1ebae1 | ||
|
|
702328a242 | ||
|
|
132391c915 | ||
|
|
15948ea863 | ||
|
|
63c2f7e748 | ||
|
|
031b2427b9 | ||
|
|
6b2a4bb066 | ||
|
|
c3d22e439f | ||
|
|
8f87764fc9 | ||
|
|
074b6feaf8 | ||
|
|
919ddf2238 | ||
|
|
dfbda326ea | ||
|
|
eff4770692 | ||
|
|
e54683c69f | ||
|
|
db22d47f72 | ||
|
|
fb75ed2c35 | ||
|
|
878092fe93 | ||
|
|
801ce61c9d | ||
|
|
0d49327cca | ||
|
|
f92d8c2f5e | ||
|
|
ce6dc58764 | ||
|
|
b4ac17585b | ||
|
|
0f65ce3dc3 | ||
|
|
d3236d27a6 | ||
|
|
5136279eb5 | ||
|
|
d63a6b7897 | ||
|
|
a9db7e087f | ||
|
|
a96cf19fd7 | ||
|
|
17cb63f792 | ||
|
|
ca1172c61c | ||
|
|
f884c16f07 | ||
|
|
d0920daa16 | ||
|
|
884f377ebc | ||
|
|
a16b3c1571 | ||
|
|
d15581e35c | ||
|
|
ca3565132d | ||
|
|
73fef65309 | ||
|
|
3494177ac5 | ||
|
|
855e4379a3 | ||
|
|
0d65ae6ea1 | ||
|
|
67aab2a1be | ||
|
|
2ba42430a3 | ||
|
|
d3cb2fa760 | ||
|
|
ed6285dcf5 | ||
|
|
7eadf6c165 | ||
|
|
04cf2e2ba2 | ||
|
|
2ba492ead2 | ||
|
|
a3b182fd45 | ||
|
|
6e30ee92a0 | ||
|
|
576b2445f8 | ||
|
|
da34775708 | ||
|
|
90afa29f34 | ||
|
|
8991733278 | ||
|
|
c213342353 | ||
|
|
a3cab14bdd | ||
|
|
f3d2b403bd | ||
|
|
32e77a4cb9 | ||
|
|
14e6501ac8 | ||
|
|
c03cf054e8 |
83
README.md
83
README.md
@@ -1,9 +1,9 @@
|
||||
**README - V2TRADING - Advanced Algorithmic Trading Platform**
|
||||
# V2TRADING - Advanced Algorithmic Trading Platform
|
||||
|
||||
**Overview**
|
||||
Custom-built algorithmic trading platform for research, backtesting and automated trading. Trading engine capable of processing tick data, managing trades, and supporting backtesting in a highly accurate and efficient manner.
|
||||
## Overview
|
||||
Custom-built algorithmic trading platform for research, backtesting and live trading. Trading engine capable of processing tick data, providing custom aggregation, managing trades, and supporting backtesting in a highly accurate and efficient manner.
|
||||
|
||||
**Key Features**
|
||||
## Key Features
|
||||
- **Trading Engine**: At the core of the platform is a trading engine that processes tick data in real time. This engine is responsible for aggregating data and managing the execution of trades, ensuring precision and speed in trade placement and execution.
|
||||
|
||||
- **High-Fidelity Backtesting Environment**: ability to backtest strategies with 1:1 precision - meaning a tick-by-tick backtesting. This level of precision in backtesting, down to millisecond accuracy, mirrors live trading environments and is vital for developing and testing high-frequency trading strategies.
|
||||
@@ -51,3 +51,78 @@ This repository represents a sophisticated and evolving tool for algorithmic tra
|
||||
</p>
|
||||
|
||||
|
||||
# Installation Instructions
|
||||
This document outlines the steps for installing and setting up the necessary environment for the application. These instructions are applicable for both Windows and Linux operating systems. Please follow the steps carefully to ensure a smooth setup.
|
||||
|
||||
## Prerequisites
|
||||
Before beginning the installation process, ensure the following prerequisites are met:
|
||||
|
||||
- TA-Lib Library:
|
||||
- Windows: Download and build the TA-Lib library. Install Visual Studio Community with the Visual C++ feature. Navigate to `C:\ta-lib\c\make\cdr\win32\msvc` in the command prompt and build the library using the available makefile.
|
||||
- Linux: Install TA-Lib using your distribution's package manager or compile from source following the instructions available on the TA-Lib GitHub repository.
|
||||
|
||||
- Alpaca Paper Trading Account: Create an account at [Alpaca Markets](https://alpaca.markets/) and generate `API_KEY` and `SECRET_KEY` for your paper trading account.
|
||||
|
||||
## Installation Steps
|
||||
**Clone the Repository:** Clone the remote repository to your local machine.
|
||||
`git clone git@github.com:drew2323/v2trading.git <name_of_local_folder>`
|
||||
|
||||
**Install Python:** Ensure Python 3.10.11 is installed on your system.
|
||||
|
||||
**Create a Virtual Environment:** Set up a Python virtual environment.
|
||||
`python -m venv <path_to_venv_folder>`
|
||||
|
||||
**Activate Virtual Environment:**
|
||||
- Windows: `source ./<venv_folder>/Scripts/activate`
|
||||
- Linux: `source ./<venv_folder>/bin/activate`
|
||||
|
||||
**Install Dependencies:** Install the program requirements.
|
||||
pip install -r requirements.txt
|
||||
Note: It's permissible to comment out references to `keras` and `tensorflow` modules, as well as the `ml-room` repository in `requirements.txt`.
|
||||
|
||||
**Environment Variables:** In `run.sh`, modify the `VIRTUAL_ENV_DIR` and `PYTHON_TO_USE` variables as necessary.
|
||||
|
||||
**Data Directory:** Navigate to `DATA_DIR` and create folders: `aggcache`, `tradecache`, and `models`.
|
||||
|
||||
**Media and Static Folders:** Create `media` and `static` folders one level above the repository directory. Also create `.env` file there.
|
||||
|
||||
**Database Setup:** Create the `v2trading.db` file using SQL commands from `v2trading_create_db.sql`.
|
||||
```
|
||||
import sqlite3
|
||||
with open("v2trading_create_db.sql", "r") as f:
|
||||
sql_statements = f.read()
|
||||
conn = sqlite3.connect('v2trading.db')
|
||||
cursor = conn.cursor()
|
||||
cursor.executescript(sql_statements)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
```
|
||||
Ensure the `config_table` is not empty by making an initial entry.
|
||||
|
||||
**Start the Application:** Run `main.py` in VSCode to start the application.
|
||||
|
||||
**Accessing the Application:** If the uvicorn server runs successfully at `http://0.0.0.0:8000`, access the application at `http://localhost:8000/static/`.
|
||||
|
||||
**Database Configuration:** Add dynamic button and JS configurations to the `config_table` in `v2trading.db` via the "Config" section on the main page.
|
||||
Please replace placeholders (e.g., `<name_of_local_folder>`, `<path_to_venv_folder>`) with your actual paths and details. Follow these instructions to ensure the application is set up correctly and ready for use.
|
||||
|
||||
## Environmental variables
|
||||
Trading platform can support N different accounts. Their API keys are stored as environmental variables in .env file located in the root directory.
|
||||
Account for trading api is selected when each strategy is run. However for realtime websocket data), always ACCOUNT1 is used for all strategies. The data point selection (iex vs sip) is set by LIVE_DATA_FEED environment variable.
|
||||
|
||||
.env file should contain:
|
||||
|
||||
```
|
||||
ACCOUNT1_LIVE_API_KEY=<ACCOUNT1_LIVE_API_KEY>
|
||||
ACCOUNT1_LIVE_SECRET_KEY=<ACCOUNT1_LIVE_SECRET_KEY>
|
||||
ACCOUNT1_LIVE_FEED=sip
|
||||
ACCOUNT1_PAPER_API_KEY=<ACCOUNT1_PAPER_API_KEY>
|
||||
ACCOUNT1_PAPER_SECRET_KEY=<ACCOUNT1_PAPER_SECRET_KEY>
|
||||
ACCOUNT1_PAPER_FEED=sip
|
||||
ACCOUNT2_PAPER_API_KEY=<ACCOUNT2_PAPER_API_KEY>
|
||||
ACCOUNT2_PAPER_SECRET_KEY=ACCOUNT2_PAPER_SECRET_KEY<>
|
||||
ACCOUNT2_PAPER_FEED=iex
|
||||
WEB_API_KEY=<pass-for-webapi>
|
||||
```
|
||||
|
||||
|
||||
|
||||
243
requirements_newest.txt
Normal file
243
requirements_newest.txt
Normal file
@@ -0,0 +1,243 @@
|
||||
absl-py
|
||||
alpaca
|
||||
alpaca-py
|
||||
altair
|
||||
annotated-types
|
||||
anyio
|
||||
appdirs
|
||||
appnope
|
||||
APScheduler
|
||||
argon2-cffi
|
||||
argon2-cffi-bindings
|
||||
arrow
|
||||
asttokens
|
||||
astunparse
|
||||
async-lru
|
||||
attrs
|
||||
Babel
|
||||
beautifulsoup4
|
||||
better-exceptions
|
||||
bleach
|
||||
blinker
|
||||
bottle
|
||||
cachetools
|
||||
CD
|
||||
certifi
|
||||
cffi
|
||||
chardet
|
||||
charset-normalizer
|
||||
click
|
||||
colorama
|
||||
comm
|
||||
contourpy
|
||||
cycler
|
||||
dash
|
||||
dash-bootstrap-components
|
||||
dash-core-components
|
||||
dash-html-components
|
||||
dash-table
|
||||
dateparser
|
||||
debugpy
|
||||
decorator
|
||||
defusedxml
|
||||
dill
|
||||
dm-tree
|
||||
entrypoints
|
||||
exceptiongroup
|
||||
executing
|
||||
fastapi
|
||||
fastjsonschema
|
||||
filelock
|
||||
Flask
|
||||
flatbuffers
|
||||
fonttools
|
||||
fpdf2
|
||||
fqdn
|
||||
gast
|
||||
gitdb
|
||||
GitPython
|
||||
google-auth
|
||||
google-auth-oauthlib
|
||||
google-pasta
|
||||
greenlet
|
||||
grpcio
|
||||
h11
|
||||
h5py
|
||||
html2text
|
||||
httpcore
|
||||
httpx
|
||||
humanize
|
||||
icecream
|
||||
idna
|
||||
imageio
|
||||
importlib-metadata
|
||||
ipykernel
|
||||
ipython
|
||||
ipywidgets
|
||||
isoduration
|
||||
itables
|
||||
itsdangerous
|
||||
jax
|
||||
jaxlib
|
||||
jedi
|
||||
Jinja2
|
||||
joblib
|
||||
json5
|
||||
jsonpointer
|
||||
jsonschema
|
||||
jsonschema-specifications
|
||||
jupyter-events
|
||||
jupyter-lsp
|
||||
jupyter_client
|
||||
jupyter_core
|
||||
jupyter_server
|
||||
jupyter_server_terminals
|
||||
jupyterlab
|
||||
jupyterlab-widgets
|
||||
jupyterlab_pygments
|
||||
jupyterlab_server
|
||||
kaleido
|
||||
keras
|
||||
keras-core
|
||||
keras-nightly
|
||||
keras-nlp-nightly
|
||||
keras-tcn @ git+https://github.com/drew2323/keras-tcn.git
|
||||
kiwisolver
|
||||
libclang
|
||||
lightweight-charts @ git+https://github.com/drew2323/lightweight-charts-python.git
|
||||
llvmlite
|
||||
Markdown
|
||||
markdown-it-py
|
||||
MarkupSafe
|
||||
matplotlib
|
||||
matplotlib-inline
|
||||
mdurl
|
||||
mistune
|
||||
ml-dtypes
|
||||
mlroom @ git+https://github.com/drew2323/mlroom.git
|
||||
mplfinance
|
||||
msgpack
|
||||
mypy-extensions
|
||||
namex
|
||||
nbclient
|
||||
nbconvert
|
||||
nbformat
|
||||
nest-asyncio
|
||||
newtulipy
|
||||
notebook_shim
|
||||
numba
|
||||
numpy
|
||||
oauthlib
|
||||
opt-einsum
|
||||
orjson
|
||||
overrides
|
||||
packaging
|
||||
pandas
|
||||
pandocfilters
|
||||
param
|
||||
parso
|
||||
patsy
|
||||
pexpect
|
||||
Pillow
|
||||
platformdirs
|
||||
plotly
|
||||
prometheus_client
|
||||
prompt-toolkit
|
||||
proto-plus
|
||||
protobuf
|
||||
proxy-tools
|
||||
psutil
|
||||
ptyprocess
|
||||
pure-eval
|
||||
pyarrow
|
||||
pyasn1
|
||||
pyasn1-modules
|
||||
pycparser
|
||||
pyct
|
||||
pydantic
|
||||
pydantic_core
|
||||
pydeck
|
||||
Pygments
|
||||
pyinstrument
|
||||
pyparsing
|
||||
pyrsistent
|
||||
pysos
|
||||
python-dateutil
|
||||
python-dotenv
|
||||
python-json-logger
|
||||
python-multipart
|
||||
pytz
|
||||
pytz-deprecation-shim
|
||||
pyviz-comms
|
||||
PyWavelets
|
||||
pywebview
|
||||
PyYAML
|
||||
pyzmq
|
||||
referencing
|
||||
regex
|
||||
requests
|
||||
requests-oauthlib
|
||||
rfc3339-validator
|
||||
rfc3986-validator
|
||||
rich
|
||||
rpds-py
|
||||
rsa
|
||||
schedule
|
||||
scikit-learn
|
||||
scipy
|
||||
seaborn
|
||||
semver
|
||||
Send2Trash
|
||||
six
|
||||
smmap
|
||||
sniffio
|
||||
soupsieve
|
||||
SQLAlchemy
|
||||
sseclient-py
|
||||
stack-data
|
||||
starlette
|
||||
statsmodels
|
||||
streamlit
|
||||
structlog
|
||||
TA-Lib
|
||||
tb-nightly
|
||||
tenacity
|
||||
tensorboard
|
||||
tensorboard-data-server
|
||||
tensorflow-addons
|
||||
tensorflow-estimator
|
||||
tensorflow-io-gcs-filesystem
|
||||
termcolor
|
||||
terminado
|
||||
tf-estimator-nightly
|
||||
tf-nightly
|
||||
tf_keras-nightly
|
||||
threadpoolctl
|
||||
tinycss2
|
||||
tinydb
|
||||
tinydb-serialization
|
||||
tinyflux
|
||||
toml
|
||||
tomli
|
||||
toolz
|
||||
tornado
|
||||
tqdm
|
||||
traitlets
|
||||
typeguard
|
||||
types-python-dateutil
|
||||
typing_extensions
|
||||
tzdata
|
||||
tzlocal
|
||||
uri-template
|
||||
urllib3
|
||||
uvicorn
|
||||
validators
|
||||
wcwidth
|
||||
webcolors
|
||||
webencodings
|
||||
websocket-client
|
||||
websockets
|
||||
Werkzeug
|
||||
widgetsnbextension
|
||||
wrapt
|
||||
zipp
|
||||
File diff suppressed because one or more lines are too long
@@ -1691,7 +1691,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.10"
|
||||
"version": "3.10.11"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
932
research/strat_SUPERTREND/SUPERTREND_v1_MULTI.ipynb
Normal file
932
research/strat_SUPERTREND/SUPERTREND_v1_MULTI.ipynb
Normal file
@@ -0,0 +1,932 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from v2realbot.tools.loadbatch import load_batch\n",
|
||||
"from v2realbot.utils.utils import zoneNY\n",
|
||||
"import pandas as pd\n",
|
||||
"import numpy as np\n",
|
||||
"import vectorbtpro as vbt\n",
|
||||
"from itables import init_notebook_mode, show\n",
|
||||
"import datetime\n",
|
||||
"from itertools import product\n",
|
||||
"from v2realbot.config import ACCOUNT1_PAPER_API_KEY, ACCOUNT1_PAPER_SECRET_KEY, DATA_DIR\n",
|
||||
"\n",
|
||||
"init_notebook_mode(all_interactive=True)\n",
|
||||
"\n",
|
||||
"vbt.settings.set_theme(\"dark\")\n",
|
||||
"vbt.settings['plotting']['layout']['width'] = 1280\n",
|
||||
"vbt.settings.plotting.auto_rangebreaks = True\n",
|
||||
"# Set the option to display with pagination\n",
|
||||
"pd.set_option('display.notebook_repr_html', True)\n",
|
||||
"pd.set_option('display.max_rows', 10) # Number of rows per page\n",
|
||||
"\n",
|
||||
"# Define the market open and close times\n",
|
||||
"market_open = datetime.time(9, 30)\n",
|
||||
"market_close = datetime.time(16, 0)\n",
|
||||
"entry_window_opens = 1\n",
|
||||
"entry_window_closes = 370\n",
|
||||
"\n",
|
||||
"forced_exit_start = 380\n",
|
||||
"forced_exit_end = 390\n",
|
||||
"\n",
|
||||
"#LOAD FROM PARQUET\n",
|
||||
"#list all files is dir directory with parquet extension\n",
|
||||
"dir = DATA_DIR + \"/notebooks/\"\n",
|
||||
"import os\n",
|
||||
"files = [f for f in os.listdir(dir) if f.endswith(\".parquet\")]\n",
|
||||
"#print('\\n'.join(map(str, files)))\n",
|
||||
"file_name = \"ohlcv_df-SPY-2024-01-01T09:30:00-2024-05-14T16:00:00.parquet\"\n",
|
||||
"ohlcv_df = pd.read_parquet(dir+file_name,engine='pyarrow')\n",
|
||||
"basic_data = vbt.Data.from_data(vbt.symbol_dict({\"SPY\": ohlcv_df}), tz_convert=zoneNY)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#parameters (primary y line, secondary y line, close)\n",
|
||||
"def plot_2y_close(priminds, secinds, close):\n",
|
||||
" fig = vbt.make_subplots(rows=1, cols=1, shared_xaxes=True, specs=[[{\"secondary_y\": True}]], vertical_spacing=0.02, subplot_titles=(\"MOM\", \"Price\" ))\n",
|
||||
" close.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False), trace_kwargs=dict(line=dict(color=\"blue\")))\n",
|
||||
" for ind in priminds:\n",
|
||||
" ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False))\n",
|
||||
" for ind in secinds:\n",
|
||||
" ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True))\n",
|
||||
" return fig\n",
|
||||
"\n",
|
||||
"# close = basic_data.xloc[\"09:30\":\"10:00\"].close"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#PIPELINE - FOR - LOOP\n",
|
||||
"\n",
|
||||
"#indicator parameters\n",
|
||||
"mom_timeperiod = list(range(2, 12))\n",
|
||||
"\n",
|
||||
"#uzavreni okna od 1 do 200\n",
|
||||
"#entry_window_closes = list(range(2, 50, 3))\n",
|
||||
"entry_window_closes = [5, 10, 30, 45]\n",
|
||||
"#entry_window_closes = 30\n",
|
||||
"#threshold entries parameters\n",
|
||||
"#long\n",
|
||||
"mom_th = np.round(np.arange(0.01, 0.5 + 0.02, 0.02),4).tolist()#-0.02\n",
|
||||
"# short\n",
|
||||
"#mom_th = np.round(np.arange(-0.01, -0.3 - 0.02, -0.02),4).tolist()#-0.02\n",
|
||||
"roc_th = np.round(np.arange(-0.2, -0.8 - 0.05, -0.05),4).tolist()#-0.2\n",
|
||||
"#print(mom_th, roc_th)\n",
|
||||
"\n",
|
||||
"#portfolio simulation parameters\n",
|
||||
"sl_stop =np.round(np.arange(0.02/100, 0.7/100, 0.05/100),4).tolist()\n",
|
||||
"tp_stop = np.round(np.arange(0.02/100, 0.7/100, 0.05/100),4).tolist()\n",
|
||||
"\n",
|
||||
"combs = list(product(mom_timeperiod, mom_th, roc_th, sl_stop, tp_stop))\n",
|
||||
"\n",
|
||||
"@vbt.parameterized(merge_func = \"concat\", random_subset = 2000, show_progress=True) \n",
|
||||
"def test_strat(entry_window_closes=60,\n",
|
||||
" mom_timeperiod=2,\n",
|
||||
" mom_th=-0.04,\n",
|
||||
" #roc_th=-0.2,\n",
|
||||
" sl_stop=0.19/100,\n",
|
||||
" tp_stop=0.19/100):\n",
|
||||
" # mom_timeperiod=2\n",
|
||||
" # mom_th=-0.06\n",
|
||||
" # roc_th=-0.2\n",
|
||||
" # sl_stop=0.04/100\n",
|
||||
" # tp_stop=0.04/100\n",
|
||||
"\n",
|
||||
" momshort = vbt.indicator(\"talib:MOM\").run(basic_data.close, timeperiod=mom_timeperiod, short_name = \"slope_short\")\n",
|
||||
" rocp = vbt.indicator(\"talib:ROC\").run(basic_data.close, short_name = \"rocp\")\n",
|
||||
" #rate of change + momentum\n",
|
||||
"\n",
|
||||
" #momshort.plot rocp.real_crossed_below(roc_th) & \n",
|
||||
" #short_signal = momshort.real_crossed_below(mom_th)\n",
|
||||
" long_signal = momshort.real_crossed_above(mom_th)\n",
|
||||
" # print(\"short signal\")\n",
|
||||
" # print(short_signal.value_counts())\n",
|
||||
"\n",
|
||||
" #forced_exit = pd.Series(False, index=close.index)\n",
|
||||
" forced_exit = basic_data.symbol_wrapper.fill(False)\n",
|
||||
" #entry_window_open = pd.Series(False, index=close.index)\n",
|
||||
" entry_window_open= basic_data.symbol_wrapper.fill(False)\n",
|
||||
"\n",
|
||||
" #print(entry_window_closes, \"entry window closes\")\n",
|
||||
" # Calculate the time difference in minutes from market open for each timestamp\n",
|
||||
" elapsed_min_from_open = (forced_exit.index.hour - market_open.hour) * 60 + (forced_exit.index.minute - market_open.minute)\n",
|
||||
"\n",
|
||||
" entry_window_open[(elapsed_min_from_open >= entry_window_opens) & (elapsed_min_from_open < entry_window_closes)] = True\n",
|
||||
"\n",
|
||||
" #print(entry_window_open.value_counts())\n",
|
||||
"\n",
|
||||
" forced_exit[(elapsed_min_from_open >= forced_exit_start) & (elapsed_min_from_open < forced_exit_end)] = True\n",
|
||||
" #short_entries = (short_signal & entry_window_open)\n",
|
||||
" #short_exits = forced_exit\n",
|
||||
" entries = (long_signal & entry_window_open)\n",
|
||||
" exits = forced_exit\n",
|
||||
" #long_entries.info()\n",
|
||||
" #number of trues and falses in long_entries\n",
|
||||
" #print(short_exits.value_counts())\n",
|
||||
" #print(short_entries.value_counts())\n",
|
||||
"\n",
|
||||
" #fig = plot_2y_close([],[momshort, rocp], close)\n",
|
||||
" #short_signal.vbt.signals.plot_as_entries(close, fig=fig, add_trace_kwargs=dict(secondary_y=False))\n",
|
||||
" #print(sl_stop)\n",
|
||||
" #tsl_th=sl_stop, \n",
|
||||
" #short_entries=short_entries, short_exits=short_exits,\n",
|
||||
" pf = vbt.Portfolio.from_signals(close=basic_data.close, entries=entries, exits=exits, tsl_stop=sl_stop, tp_stop = tp_stop, fees=0.0167/100, freq=\"1s\", price=\"close\") #sl_stop=sl_stop, tp_stop = sl_stop,\n",
|
||||
" \n",
|
||||
" return pf.stats([\n",
|
||||
" 'total_return',\n",
|
||||
" 'max_dd', \n",
|
||||
" 'total_trades', \n",
|
||||
" 'win_rate', \n",
|
||||
" 'expectancy'\n",
|
||||
" ])\n",
|
||||
"\n",
|
||||
"pf_results = test_strat(vbt.Param(entry_window_closes),\n",
|
||||
" vbt.Param(mom_timeperiod),\n",
|
||||
" vbt.Param(mom_th),\n",
|
||||
" #vbt.Param(roc_th)\n",
|
||||
" vbt.Param(sl_stop),\n",
|
||||
" vbt.Param(tp_stop, condition=\"tp_stop > sl_stop\"))\n",
|
||||
"pf_results = pf_results.unstack(level=-1)\n",
|
||||
"pf_results.sort_values(by=[\"Total Return [%]\", \"Max Drawdown [%]\"], ascending=[False, True])\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#pf_results.load(\"10tiscomb.pickle\")\n",
|
||||
"#pf_results.info()\n",
|
||||
"\n",
|
||||
"vbt.save(pf_results, \"8tiscomb_tsl.pickle\")\n",
|
||||
"\n",
|
||||
"# pf_results = vbt.load(\"8tiscomb_tsl.pickle\")\n",
|
||||
"# pf_results\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# parallel_coordinates method¶\n",
|
||||
"\n",
|
||||
"# attach_px_methods.<locals>.plot_func(\n",
|
||||
"# *args,\n",
|
||||
"# layout=None,\n",
|
||||
"# **kwargs\n",
|
||||
"# )\n",
|
||||
"\n",
|
||||
"# pf_results.vbt.px.parallel_coordinates() #ocdf\n",
|
||||
"\n",
|
||||
"res = pf_results.reset_index()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"pf_results"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import pandas as pd\n",
|
||||
"from sklearn.decomposition import PCA\n",
|
||||
"from sklearn.preprocessing import StandardScaler\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"\n",
|
||||
"# Assuming pf_results is your DataFrame\n",
|
||||
"# Convert columns to numeric, assuming NaNs where conversion fails\n",
|
||||
"metrics = ['Total Return [%]', 'Max Drawdown [%]', 'Total Trades']\n",
|
||||
"for metric in metrics:\n",
|
||||
" pf_results[metric] = pd.to_numeric(pf_results[metric], errors='coerce')\n",
|
||||
"\n",
|
||||
"# Handle missing values, for example filling with the median\n",
|
||||
"pf_results['Max Drawdown [%]'].fillna(pf_results['Max Drawdown [%]'].median(), inplace=True)\n",
|
||||
"\n",
|
||||
"# Extract the metrics into a new DataFrame\n",
|
||||
"data_for_pca = pf_results[metrics]\n",
|
||||
"\n",
|
||||
"# Standardize the data before applying PCA\n",
|
||||
"scaler = StandardScaler()\n",
|
||||
"data_scaled = scaler.fit_transform(data_for_pca)\n",
|
||||
"\n",
|
||||
"# Apply PCA\n",
|
||||
"pca = PCA(n_components=2) # Adjust components as needed\n",
|
||||
"principal_components = pca.fit_transform(data_scaled)\n",
|
||||
"\n",
|
||||
"# Create a DataFrame with the principal components\n",
|
||||
"pca_results = pd.DataFrame(data=principal_components, columns=['PC1', 'PC2'])\n",
|
||||
"\n",
|
||||
"# Visualize the results\n",
|
||||
"plt.figure(figsize=(8,6))\n",
|
||||
"plt.scatter(pca_results['PC1'], pca_results['PC2'], alpha=0.5)\n",
|
||||
"plt.xlabel('Principal Component 1')\n",
|
||||
"plt.ylabel('Principal Component 2')\n",
|
||||
"plt.title('PCA of Strategy Optimization Results')\n",
|
||||
"plt.grid(True)\n",
|
||||
"plt.savefig(\"ddd.png\")\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Check if there is any unnamed level and rename it\n",
|
||||
"if None in df.index.names:\n",
|
||||
" # Generate new names list replacing None with 'stat'\n",
|
||||
" new_names = ['stat' if name is None else name for name in df.index.names]\n",
|
||||
" df.index.set_names(new_names, inplace=True)\n",
|
||||
"\n",
|
||||
"rs= df\n",
|
||||
"\n",
|
||||
"rs.info()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# # Now, 'stat' is the name of the previously unnamed level\n",
|
||||
"\n",
|
||||
"# # Filter for 'Total Return' assuming it is a correct identifier in the 'stat' level\n",
|
||||
"# total_return_series = df.xs('Total Return [%]', level='stat')\n",
|
||||
"\n",
|
||||
"# # Sort the Series to get the largest 'Total Return' values\n",
|
||||
"# sorted_series = total_return_series.sort_values(ascending=False)\n",
|
||||
"\n",
|
||||
"# # Print the sorted filtered data\n",
|
||||
"# sorted_series.head(20)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"sorted_series.vbt.save()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#df.info()\n",
|
||||
"total_return_series = df.xs('Total Return [%]')\n",
|
||||
"sorted_series = total_return_series.sort_values(ascending=False)\n",
|
||||
"\n",
|
||||
"# Display the top N entries, e.g., top 5\n",
|
||||
"sorted_series.head(5)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"df"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"comb_stats_df.nlargest(10, 'Total Return [%]')\n",
|
||||
"#stats_df.info()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"8\t-0.06\t-0.2\t0.0028\t0.0048\t4.156254\n",
|
||||
"4 -0.02 -0.25 0.0028 0.0048 0.84433\n",
|
||||
"3 -0.02 -0.25 0.0033 0.0023 Total Return [%] 0.846753\n",
|
||||
"#2\t-0.04\t-0.2\t0.0019\t0.0019\n",
|
||||
"# 2\t-0.04\t-0.2\t0.0019\t0.0019\t0.556919\t91\t60.43956\t0.00612\n",
|
||||
"# 2\t-0.04\t-0.25\t0.0019\t0.0019\t0.556919\t91\t60.43956\t0.00612\n",
|
||||
"# 2\t-0.04\t-0.3\t0.0019\t0.0019\t0.556919\t91\t60.43956\t0.00612\n",
|
||||
"# 2\t-0.04\t-0.35\t0.0019\t0.0019\t0.556919\t91\t60.43956\t0.00612\n",
|
||||
"# 2\t-0.04\t-0.4\t0.0019\t0.0019\t0.556919\t91\t60.43956\t0.00612\n",
|
||||
"# 2\t-0.04\t-0.2\t0.0019\t0.0017\t0.451338\t93\t63.44086\t0.004853\n",
|
||||
"# 2\t-0.04\t-0.25\t0.0019\t0.0017\t0.451338\t93\t63.44086\t0.004853\n",
|
||||
"# 2\t-0.04\t-0.3\t0.0019\t0.0017\t0.451338\t93\t63.44086\t0.004853\n",
|
||||
"# 2\t-0.04\t-0.35\t0.0019\t0.0017\t0.451338\t93\t63.44086\t0.004853\n",
|
||||
"# 2\t-0.04\t-0.4\t0.0019\t0.0017\t0.451338\t93\t63.44086\t0.004853"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"pf.plot()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"basic_data.symbols"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
">>> def apply_func(ts, entries, exits, fastw, sloww, minp=None):\n",
|
||||
"... fast_ma = vbt.nb.rolling_mean_nb(ts, fastw, minp=minp)\n",
|
||||
"... slow_ma = vbt.nb.rolling_mean_nb(ts, sloww, minp=minp)\n",
|
||||
"... entries[:] = vbt.nb.crossed_above_nb(fast_ma, slow_ma) \n",
|
||||
"... exits[:] = vbt.nb.crossed_above_nb(slow_ma, fast_ma)\n",
|
||||
"... return (fast_ma, slow_ma) \n",
|
||||
"\n",
|
||||
">>> CrossSig = vbt.IF(\n",
|
||||
"... class_name=\"CrossSig\",\n",
|
||||
"... input_names=['ts'],\n",
|
||||
"... in_output_names=['entries', 'exits'],\n",
|
||||
"... param_names=['fastw', 'sloww'],\n",
|
||||
"... output_names=['fast_ma', 'slow_ma']\n",
|
||||
"... ).with_apply_func(\n",
|
||||
"... apply_func,\n",
|
||||
"... in_output_settings=dict(\n",
|
||||
"... entries=dict(dtype=np.bool_), #initialize output with bool\n",
|
||||
"... exits=dict(dtype=np.bool_)\n",
|
||||
"... )\n",
|
||||
"... )\n",
|
||||
">>> cross_sig = CrossSig.run(ts2, 2, 4)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#PIPELINE - parameters in one go\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"#TOTO prepsat do FOR-LOOPu\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"#indicator parameters\n",
|
||||
"mom_timeperiod = list(range(2, 6))\n",
|
||||
"\n",
|
||||
"#threshold entries parameters\n",
|
||||
"mom_th = np.round(np.arange(-0.02, -0.1 - 0.02, -0.02),4).tolist()#-0.02\n",
|
||||
"roc_th = np.round(np.arange(-0.2, -0.4 - 0.05, -0.05),4).tolist()#-0.2\n",
|
||||
"#print(mom_th, roc_th)\n",
|
||||
"#jejich product\n",
|
||||
"# mom_th_prod, roc_th_prod = zip(*product(mom_th, roc_th))\n",
|
||||
"\n",
|
||||
"# #convert threshold to vbt param\n",
|
||||
"# mom_th_index = vbt.Param(mom_th_prod, name='mom_th_th') \n",
|
||||
"# roc_th_index = vbt.Param(roc_th_prod, name='roc_th_th')\n",
|
||||
"\n",
|
||||
"mom_th = vbt.Param(mom_th, name='mom_th')\n",
|
||||
"roc_th = vbt.Param(roc_th, name='roc_th')\n",
|
||||
"\n",
|
||||
"#portfolio simulation parameters\n",
|
||||
"sl_stop = np.arange(0.03/100, 0.2/100, 0.02/100).tolist()\n",
|
||||
"# Using the round function\n",
|
||||
"sl_stop = [round(val, 4) for val in sl_stop]\n",
|
||||
"tp_stop = np.arange(0.03/100, 0.2/100, 0.02/100).tolist()\n",
|
||||
"# Using the round function\n",
|
||||
"tp_stop = [round(val, 4) for val in tp_stop]\n",
|
||||
"sl_stop = vbt.Param(sl_stop) #np.nan mean s no stoploss\n",
|
||||
"tp_stop = vbt.Param(tp_stop) #np.nan mean s no stoploss\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"#def test_mom(window=14, mom_th=0.2, roc_th=0.2, sl_stop=0.03/100, tp_stop=0.03/100):\n",
|
||||
"#close = basic_data.xloc[\"09:30\":\"10:00\"].close\n",
|
||||
"momshort = vbt.indicator(\"talib:MOM\").run(basic_data.get(\"Close\"), timeperiod=mom_timeperiod, short_name = \"slope_short\")\n",
|
||||
"\n",
|
||||
"#ht_trendline = vbt.indicator(\"talib:HT_TRENDLINE\").run(close, short_name = \"httrendline\")\n",
|
||||
"rocp = vbt.indicator(\"talib:ROC\").run(basic_data.get(\"Close\"), short_name = \"rocp\")\n",
|
||||
"#rate of change + momentum\n",
|
||||
"\n",
|
||||
"rocp_signal = rocp.real_crossed_below(mom_th)\n",
|
||||
"mom_signal = momshort.real_crossed_below(roc_th)\n",
|
||||
"\n",
|
||||
"#mom_signal\n",
|
||||
"print(rocp_signal.info())\n",
|
||||
"print(mom_signal.info())\n",
|
||||
"#print(rocp.real)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"short_signal = (mom_signal.vbt & rocp_signal)\n",
|
||||
"\n",
|
||||
"# #short_signal = (rocp.real_crossed_below(roc_th_index) & momshort.real_crossed_below(mom_th_index))\n",
|
||||
"# forced_exit = m1_data.symbol_wrapper.fill(False)\n",
|
||||
"# entry_window_open= m1_data.symbol_wrapper.fill(False)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# # Calculate the time difference in minutes from market open for each timestamp\n",
|
||||
"# elapsed_min_from_open = (forced_exit.index.hour - market_open.hour) * 60 + (forced_exit.index.minute - market_open.minute)\n",
|
||||
"\n",
|
||||
"# entry_window_open[(elapsed_min_from_open >= entry_window_opens) & (elapsed_min_from_open < entry_window_closes)] = True\n",
|
||||
"# forced_exit[(elapsed_min_from_open >= forced_exit_start) & (elapsed_min_from_open < forced_exit_end)] = True\n",
|
||||
"# short_entries = (short_signal & entry_window_open)\n",
|
||||
"# short_exits = forced_exit\n",
|
||||
"# #long_entries.info()\n",
|
||||
"# #number of trues and falses in long_entries\n",
|
||||
"# #short_exits.value_counts()\n",
|
||||
"# #short_entries.value_counts()\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# pf = vbt.Portfolio.from_signals(close=close, short_entries=short_entries, short_exits=short_exits, sl_stop=sl_stop, tp_stop = tp_stop, fees=0.0167/100, freq=\"1s\") #sl_stop=sl_stop, tp_stop = sl_stop,\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# filter dates"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#filter na dny\n",
|
||||
"dates_of_interest = pd.to_datetime(['2024-04-22']).tz_localize('US/Eastern')\n",
|
||||
"filtered_df = df.loc[df.index.normalize().isin(dates_of_interest)]\n",
|
||||
"\n",
|
||||
"df = filtered_df\n",
|
||||
"df.info()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# import plotly.io as pio\n",
|
||||
"# pio.renderers.default = 'notebook'\n",
|
||||
"\n",
|
||||
"#naloadujeme do vbt symbol as column\n",
|
||||
"basic_data = vbt.Data.from_data({\"BAC\": df}, tz_convert=zoneNY)\n",
|
||||
"\n",
|
||||
"vbt.settings.plotting.auto_rangebreaks = True\n",
|
||||
"#basic_data.data[\"BAC\"].vbt.ohlcv.plot()\n",
|
||||
"\n",
|
||||
"#basic_data.data[\"BAC\"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"m1_data = basic_data[['Open', 'High', 'Low', 'Close', 'Volume']]\n",
|
||||
"\n",
|
||||
"m1_data.data[\"BAC\"]\n",
|
||||
"#m5_data = m1_data.resample(\"5T\")\n",
|
||||
"\n",
|
||||
"#m5_data.data[\"BAC\"].head(10)\n",
|
||||
"\n",
|
||||
"# m15_data = m1_data.resample(\"15T\")\n",
|
||||
"\n",
|
||||
"# m15 = m15_data.data[\"BAC\"]\n",
|
||||
"\n",
|
||||
"# m15.vbt.ohlcv.plot()\n",
|
||||
"\n",
|
||||
"# m1_data.wrapper.index\n",
|
||||
"\n",
|
||||
"# m1_resampler = m1_data.wrapper.get_resampler(\"1T\")\n",
|
||||
"# m1_resampler.index_difference(reverse=True)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# m5_resampler.prettify()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# MOM indicator"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"vbt.phelp(vbt.indicator(\"talib:ROCP\").run)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"vyuzití rychleho klesani na sekundove urovni behem open rush\n",
|
||||
"- MOM + ROC during open rush\n",
|
||||
"- short signal\n",
|
||||
"- pipeline kombinace thresholdu pro vstup mom_th, roc_th + hodnota sl_stop a tp_stop (pripadne trailing) - nalezeni optimalni kombinace atributu"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# fig = plot_2y_close([ht_trendline],[momshort, rocp], close)\n",
|
||||
"# short_signal.vbt.signals.plot_as_entries(close, fig=fig, add_trace_kwargs=dict(secondary_y=False)) \n",
|
||||
"\n",
|
||||
"#parameters (primary y line, secondary y line, close)\n",
|
||||
"def plot_2y_close(priminds, secinds, close):\n",
|
||||
" fig = vbt.make_subplots(rows=1, cols=1, shared_xaxes=True, specs=[[{\"secondary_y\": True}]], vertical_spacing=0.02, subplot_titles=(\"MOM\", \"Price\" ))\n",
|
||||
" close.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False), trace_kwargs=dict(line=dict(color=\"blue\")))\n",
|
||||
" for ind in priminds:\n",
|
||||
" ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False))\n",
|
||||
" for ind in secinds:\n",
|
||||
" ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True))\n",
|
||||
" return fig\n",
|
||||
"\n",
|
||||
"close = m1_data.xloc[\"09:30\":\"10:00\"].close\n",
|
||||
"momshort = vbt.indicator(\"talib:MOM\").run(close, timeperiod=3, short_name = \"slope_short\")\n",
|
||||
"ht_trendline = vbt.indicator(\"talib:HT_TRENDLINE\").run(close, short_name = \"httrendline\")\n",
|
||||
"rocp = vbt.indicator(\"talib:ROC\").run(close, short_name = \"rocp\")\n",
|
||||
"#rate of change + momentum\n",
|
||||
"short_signal = (rocp.real_crossed_below(-0.2) & momshort.real_crossed_below(-0.02))\n",
|
||||
"#indlong = vbt.indicator(\"talib:MOM\").run(close, timeperiod=10, short_name = \"slope_long\")\n",
|
||||
"fig = plot_2y_close([ht_trendline],[momshort, rocp], close)\n",
|
||||
"short_signal.vbt.signals.plot_as_entries(close, fig=fig, add_trace_kwargs=dict(secondary_y=False)) "
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"close = m1_data.close\n",
|
||||
"#vbt.phelp(vbt.OLS.run)\n",
|
||||
"\n",
|
||||
"#oer steepmnes of regression line\n",
|
||||
"#talib.LINEARREG_SLOPE(close, timeperiod=timeperiod)\n",
|
||||
"#a také ON BALANCE VOLUME - http://5.161.179.223:8000/static/js/vbt/api/indicators/custom/obv/index.html\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"mom_ind = vbt.indicator(\"talib:MOM\") \n",
|
||||
"#vbt.phelp(mom_ind.run)\n",
|
||||
"\n",
|
||||
"mom = mom_ind.run(close, timeperiod=10)\n",
|
||||
"\n",
|
||||
"plot_2y_close(mom, close)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# defining ENTRY WINDOW and forced EXIT window"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#m1_data.data[\"BAC\"].info()\n",
|
||||
"import datetime\n",
|
||||
"# Define the market open and close times\n",
|
||||
"market_open = datetime.time(9, 30)\n",
|
||||
"market_close = datetime.time(16, 0)\n",
|
||||
"entry_window_opens = 2\n",
|
||||
"entry_window_closes = 30\n",
|
||||
"\n",
|
||||
"forced_exit_start = 380\n",
|
||||
"forced_exit_end = 390\n",
|
||||
"\n",
|
||||
"forced_exit = m1_data.symbol_wrapper.fill(False)\n",
|
||||
"entry_window_open= m1_data.symbol_wrapper.fill(False)\n",
|
||||
"\n",
|
||||
"# Calculate the time difference in minutes from market open for each timestamp\n",
|
||||
"elapsed_min_from_open = (forced_exit.index.hour - market_open.hour) * 60 + (forced_exit.index.minute - market_open.minute)\n",
|
||||
"\n",
|
||||
"entry_window_open[(elapsed_min_from_open >= entry_window_opens) & (elapsed_min_from_open < entry_window_closes)] = True\n",
|
||||
"forced_exit[(elapsed_min_from_open >= forced_exit_start) & (elapsed_min_from_open < forced_exit_end)] = True\n",
|
||||
"\n",
|
||||
"#entry_window_open.info()\n",
|
||||
"# forced_exit.tail(100)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"close = m1_data.close\n",
|
||||
"\n",
|
||||
"#rsi = vbt.RSI.run(close, window=14)\n",
|
||||
"\n",
|
||||
"short_entries = (short_signal & entry_window_open)\n",
|
||||
"short_exits = forced_exit\n",
|
||||
"#long_entries.info()\n",
|
||||
"#number of trues and falses in long_entries\n",
|
||||
"#short_exits.value_counts()\n",
|
||||
"short_entries.value_counts()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"def plot_rsi(close, entries, exits):\n",
|
||||
" fig = vbt.make_subplots(rows=1, cols=1, shared_xaxes=True, specs=[[{\"secondary_y\": True}]], vertical_spacing=0.02, subplot_titles=(\"RSI\", \"Price\" ))\n",
|
||||
" close.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True))\n",
|
||||
" #rsi.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False))\n",
|
||||
" entries.vbt.signals.plot_as_entries(close, fig=fig, add_trace_kwargs=dict(secondary_y=False)) \n",
|
||||
" exits.vbt.signals.plot_as_exits(close, fig=fig, add_trace_kwargs=dict(secondary_y=False)) \n",
|
||||
" return fig\n",
|
||||
"\n",
|
||||
"plot_rsi(close, short_entries, short_exits)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"vbt.phelp(vbt.Portfolio.from_signals)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"sl_stop = np.arange(0.03/100, 0.2/100, 0.02/100).tolist()\n",
|
||||
"# Using the round function\n",
|
||||
"sl_stop = [round(val, 4) for val in sl_stop]\n",
|
||||
"print(sl_stop)\n",
|
||||
"sl_stop = vbt.Param(sl_stop) #np.nan mean s no stoploss\n",
|
||||
"\n",
|
||||
"pf = vbt.Portfolio.from_signals(close=close, short_entries=short_entries, short_exits=short_exits, sl_stop=0.03/100, tp_stop = 0.03/100, fees=0.0167/100, freq=\"1s\") #sl_stop=sl_stop, tp_stop = sl_stop,\n",
|
||||
"\n",
|
||||
"#pf.stats()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#list of orders\n",
|
||||
"#pf.orders.records_readable\n",
|
||||
"#pf.orders.plots()\n",
|
||||
"#pf.stats()\n",
|
||||
"pf.stats()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"pf.plot()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"pf[(0.0015,0.0013)].plot()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"pf[0.03].plot_trade_signals()\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# pristup k pf jako multi index"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#pf[0.03].plot()\n",
|
||||
"#pf.order_records\n",
|
||||
"pf[(0.03)].stats()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#zgrupovane statistiky\n",
|
||||
"stats_df = pf.stats([\n",
|
||||
" 'total_return',\n",
|
||||
" 'total_trades',\n",
|
||||
" 'win_rate',\n",
|
||||
" 'expectancy'\n",
|
||||
"], agg_func=None)\n",
|
||||
"stats_df\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"stats_df.nlargest(10, 'Total Return [%]')\n",
|
||||
"#stats_df.info()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"pf[(0.0011,0.0013000000000000002)].plot()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from pandas.tseries.offsets import DateOffset\n",
|
||||
"\n",
|
||||
"temp_data = basic_data['2024-4-22']\n",
|
||||
"temp_data\n",
|
||||
"res1m = temp_data[[\"Open\", \"High\", \"Low\", \"Close\", \"Volume\"]]\n",
|
||||
"\n",
|
||||
"# Define a custom date offset that starts at 9:30 AM and spans 4 hours\n",
|
||||
"custom_offset = DateOffset(hours=4, minutes=30)\n",
|
||||
"\n",
|
||||
"# res1m = res1m.get().resample(\"4H\").agg({ \n",
|
||||
"# \"Open\": \"first\",\n",
|
||||
"# \"High\": \"max\",\n",
|
||||
"# \"Low\": \"min\",\n",
|
||||
"# \"Close\": \"last\",\n",
|
||||
"# \"Volume\": \"sum\"\n",
|
||||
"# })\n",
|
||||
"\n",
|
||||
"res4h = res1m.resample(\"1h\", resample_kwargs=dict(origin=\"start\"))\n",
|
||||
"\n",
|
||||
"res4h.data\n",
|
||||
"\n",
|
||||
"res15m = res1m.resample(\"15T\", resample_kwargs=dict(origin=\"start\"))\n",
|
||||
"\n",
|
||||
"res15m.data[\"BAC\"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"@vbt.njit\n",
|
||||
"def long_entry_place_func_nb(c, low, close, time_in_ns, rsi14, window_open, window_close):\n",
|
||||
" market_open_minutes = 570 # 9 hours * 60 minutes + 30 minutes\n",
|
||||
"\n",
|
||||
" for out_i in range(len(c.out)):\n",
|
||||
" i = c.from_i + out_i\n",
|
||||
"\n",
|
||||
" current_minutes = vbt.dt_nb.hour_nb(time_in_ns[i]) * 60 + vbt.dt_nb.minute_nb(time_in_ns[i])\n",
|
||||
" #print(\"current_minutes\", current_minutes)\n",
|
||||
" # Calculate elapsed minutes since market open at 9:30 AM\n",
|
||||
" elapsed_from_open = current_minutes - market_open_minutes\n",
|
||||
" elapsed_from_open = elapsed_from_open if elapsed_from_open >= 0 else 0\n",
|
||||
" #print( \"elapsed_from_open\", elapsed_from_open)\n",
|
||||
"\n",
|
||||
" #elapsed_from_open = elapsed_minutes_from_open_nb(time_in_ns) \n",
|
||||
" in_window = elapsed_from_open > window_open and elapsed_from_open < window_close\n",
|
||||
" #print(\"in_window\", in_window)\n",
|
||||
" # if in_window:\n",
|
||||
" # print(\"in window\")\n",
|
||||
"\n",
|
||||
" if in_window and rsi14[i] > 60: # and low[i, c.col] <= hit_price: # and hour == 9: # (4)!\n",
|
||||
" return out_i\n",
|
||||
" return -1\n",
|
||||
"\n",
|
||||
"@vbt.njit\n",
|
||||
"def long_exit_place_func_nb(c, high, close, time_index, tp, sl): # (5)!\n",
|
||||
" entry_i = c.from_i - c.wait\n",
|
||||
" entry_price = close[entry_i, c.col]\n",
|
||||
" hit_price = entry_price * (1 + tp)\n",
|
||||
" stop_price = entry_price * (1 - sl)\n",
|
||||
" for out_i in range(len(c.out)):\n",
|
||||
" i = c.from_i + out_i\n",
|
||||
" last_bar_of_day = vbt.dt_nb.day_changed_nb(time_index[i], time_index[i + 1])\n",
|
||||
"\n",
|
||||
" #print(next_day)\n",
|
||||
" if last_bar_of_day: #pokud je dalsi next day, tak zavirame posledni\n",
|
||||
" print(\"ted\",out_i)\n",
|
||||
" return out_i\n",
|
||||
" if close[i, c.col] >= hit_price or close[i, c.col] <= stop_price :\n",
|
||||
" return out_i\n",
|
||||
" return -1\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"df = pd.DataFrame(np.random.random(size=(5, 10)), columns=list('abcdefghij'))\n",
|
||||
"\n",
|
||||
"df"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"df.sum()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": ".venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.11"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
782
research/strat_SUPERTREND/SUPERTREND_v1_SINGLE.ipynb
Normal file
782
research/strat_SUPERTREND/SUPERTREND_v1_SINGLE.ipynb
Normal file
@@ -0,0 +1,782 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# SUPERTREND\n",
|
||||
"\n",
|
||||
"* kombinace supertrendu na vice urovnich"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 9,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from dotenv import load_dotenv\n",
|
||||
"\n",
|
||||
"#as V2realbot is client , load env variables here\n",
|
||||
"env_file = \"/Users/davidbrazda/Documents/Development/python/.env\"\n",
|
||||
"# Load the .env file\n",
|
||||
"load_dotenv(env_file)\n",
|
||||
"\n",
|
||||
"from v2realbot.utils.utils import zoneNY\n",
|
||||
"import pandas as pd\n",
|
||||
"import numpy as np\n",
|
||||
"import vectorbtpro as vbt\n",
|
||||
"# from itables import init_notebook_mode, show\n",
|
||||
"import datetime\n",
|
||||
"from itertools import product\n",
|
||||
"from v2realbot.config import DATA_DIR\n",
|
||||
"from lightweight_charts import JupyterChart, chart, Panel, PlotAccessor\n",
|
||||
"from IPython.display import display\n",
|
||||
"\n",
|
||||
"# init_notebook_mode(all_interactive=True)\n",
|
||||
"\n",
|
||||
"vbt.settings.set_theme(\"dark\")\n",
|
||||
"vbt.settings['plotting']['layout']['width'] = 1280\n",
|
||||
"vbt.settings.plotting.auto_rangebreaks = True\n",
|
||||
"# Set the option to display with pagination\n",
|
||||
"pd.set_option('display.notebook_repr_html', True)\n",
|
||||
"pd.set_option('display.max_rows', 10) # Number of rows per page"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"trades_df-BAC-2024-01-01T09_30_00-2024-05-14T16_00_00-CO4B7VPWUZF-100.parquet\n",
|
||||
"trades_df-BAC-2024-01-11T09:30:00-2024-01-12T16:00:00.parquet\n",
|
||||
"trades_df-SPY-2024-01-01T09:30:00-2024-05-14T16:00:00.parquet\n",
|
||||
"trades_df-BAC-2023-01-01T09_30_00-2024-05-25T16_00_00-47BCFOPUVWZ-100.parquet\n",
|
||||
"ohlcv_df-BAC-2024-01-11T09:30:00-2024-01-12T16:00:00.parquet\n",
|
||||
"trades_df-BAC-2024-05-15T09_30_00-2024-05-25T16_00_00-47BCFOPUVWZ-100.parquet\n",
|
||||
"ohlcv_df-BAC-2024-01-01T09_30_00-2024-05-25T16_00_00-47BCFOPUVWZ-100.parquet\n",
|
||||
"ohlcv_df-SPY-2024-01-01T09:30:00-2024-05-14T16:00:00.parquet\n",
|
||||
"ohlcv_df-BAC-2024-01-01T09_30_00-2024-05-14T16_00_00-CO4B7VPWUZF-100.parquet\n",
|
||||
"ohlcv_df-BAC-2023-01-01T09_30_00-2024-05-25T16_00_00-47BCFOPUVWZ-100.parquet\n",
|
||||
"ohlcv_df-BAC-2023-01-01T09_30_00-2024-05-25T15_30_00-47BCFOPUVWZ-100.parquet\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"351"
|
||||
]
|
||||
},
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# Define the market open and close times\n",
|
||||
"market_open = datetime.time(9, 30)\n",
|
||||
"market_close = datetime.time(16, 0)\n",
|
||||
"entry_window_opens = 1\n",
|
||||
"entry_window_closes = 370\n",
|
||||
"forced_exit_start = 380\n",
|
||||
"forced_exit_end = 390\n",
|
||||
"\n",
|
||||
"#LOAD FROM PARQUET\n",
|
||||
"#list all files is dir directory with parquet extension\n",
|
||||
"dir = DATA_DIR + \"/notebooks/\"\n",
|
||||
"import os\n",
|
||||
"files = [f for f in os.listdir(dir) if f.endswith(\".parquet\")]\n",
|
||||
"print('\\n'.join(map(str, files)))\n",
|
||||
"file_name = \"ohlcv_df-BAC-2023-01-01T09_30_00-2024-05-25T15_30_00-47BCFOPUVWZ-100.parquet\"\n",
|
||||
"ohlcv_df = pd.read_parquet(dir+file_name,engine='pyarrow')\n",
|
||||
"#filter ohlcv_df to certain date range (assuming datetime index)\n",
|
||||
"#ohlcv_df = ohlcv_df.loc[\"2024-02-12 9:30\":\"2024-02-14 16:00\"]\n",
|
||||
"\n",
|
||||
"#add vwap column to ohlcv_df\n",
|
||||
"#ohlcv_df[\"hlcc4\"] = (ohlcv_df[\"close\"] + ohlcv_df[\"high\"] + ohlcv_df[\"low\"] + ohlcv_df[\"close\"]) / 4\n",
|
||||
"\n",
|
||||
"basic_data = vbt.Data.from_data(vbt.symbol_dict({\"BAC\": ohlcv_df}), tz_convert=zoneNY)\n",
|
||||
"ohlcv_df= None\n",
|
||||
"basic_data.wrapper.index.normalize().nunique()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"<class 'pandas.core.frame.DataFrame'>\n",
|
||||
"DatetimeIndex: 4549772 entries, 2023-01-03 09:30:01-05:00 to 2024-05-24 15:59:59-04:00\n",
|
||||
"Data columns (total 10 columns):\n",
|
||||
" # Column Dtype \n",
|
||||
"--- ------ ----- \n",
|
||||
" 0 open float64 \n",
|
||||
" 1 high float64 \n",
|
||||
" 2 low float64 \n",
|
||||
" 3 close float64 \n",
|
||||
" 4 volume float64 \n",
|
||||
" 5 trades float64 \n",
|
||||
" 6 updated datetime64[ns, US/Eastern]\n",
|
||||
" 7 vwap float64 \n",
|
||||
" 8 buyvolume float64 \n",
|
||||
" 9 sellvolume float64 \n",
|
||||
"dtypes: datetime64[ns, US/Eastern](1), float64(9)\n",
|
||||
"memory usage: 381.8 MB\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"basic_data.data[\"BAC\"].info()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"Add resample function to custom columns"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from vectorbtpro.utils.config import merge_dicts, Config, HybridConfig\n",
|
||||
"from vectorbtpro import _typing as tp\n",
|
||||
"from vectorbtpro.generic import nb as generic_nb\n",
|
||||
"\n",
|
||||
"_feature_config: tp.ClassVar[Config] = HybridConfig(\n",
|
||||
" {\n",
|
||||
" \"buyvolume\": dict(\n",
|
||||
" resample_func=lambda self, obj, resampler: obj.vbt.resample_apply(\n",
|
||||
" resampler,\n",
|
||||
" generic_nb.sum_reduce_nb,\n",
|
||||
" )\n",
|
||||
" ),\n",
|
||||
" \"sellvolume\": dict(\n",
|
||||
" resample_func=lambda self, obj, resampler: obj.vbt.resample_apply(\n",
|
||||
" resampler,\n",
|
||||
" generic_nb.sum_reduce_nb,\n",
|
||||
" )\n",
|
||||
" ),\n",
|
||||
" \"trades\": dict(\n",
|
||||
" resample_func=lambda self, obj, resampler: obj.vbt.resample_apply(\n",
|
||||
" resampler,\n",
|
||||
" generic_nb.sum_reduce_nb,\n",
|
||||
" )\n",
|
||||
" )\n",
|
||||
" }\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"basic_data._feature_config = _feature_config"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"s1data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','trades','sellvolume']]\n",
|
||||
"\n",
|
||||
"s5data = s1data.resample(\"5s\")\n",
|
||||
"s5data = s5data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
|
||||
"\n",
|
||||
"t1data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','trades','sellvolume']].resample(\"1T\")\n",
|
||||
"t1data = t1data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
|
||||
"# t1data.data[\"BAC\"].info()\n",
|
||||
"\n",
|
||||
"t30data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','trades','sellvolume']].resample(\"30T\")\n",
|
||||
"t30data = t30data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
|
||||
"# t30data.data[\"BAC\"].info()\n",
|
||||
"\n",
|
||||
"s1close = s1data.close\n",
|
||||
"t1close = t1data.close\n",
|
||||
"t30close = t30data.close\n",
|
||||
"t30volume = t30data.volume\n",
|
||||
"\n",
|
||||
"#resample on specific index \n",
|
||||
"resampler = vbt.Resampler(t30data.index, s1data.index, source_freq=\"30T\", target_freq=\"1s\")\n",
|
||||
"t30close_realigned = t30close.vbt.realign_closing(resampler)\n",
|
||||
"\n",
|
||||
"#resample 1min to s\n",
|
||||
"resampler_s = vbt.Resampler(t1data.index, s1data.index, source_freq=\"1T\", target_freq=\"1s\")\n",
|
||||
"t1close_realigned = t1close.vbt.realign_closing(resampler_s)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"datetime64[ns, US/Eastern]\n",
|
||||
"datetime64[ns, US/Eastern]\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(t30data.index.dtype)\n",
|
||||
"print(s1data.index.dtype)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"<class 'pandas.core.frame.DataFrame'>\n",
|
||||
"DatetimeIndex: 4551 entries, 2023-01-03 09:30:00-05:00 to 2024-05-24 15:30:00-04:00\n",
|
||||
"Data columns (total 9 columns):\n",
|
||||
" # Column Non-Null Count Dtype \n",
|
||||
"--- ------ -------------- ----- \n",
|
||||
" 0 open 4551 non-null float64\n",
|
||||
" 1 high 4551 non-null float64\n",
|
||||
" 2 low 4551 non-null float64\n",
|
||||
" 3 close 4551 non-null float64\n",
|
||||
" 4 volume 4551 non-null float64\n",
|
||||
" 5 vwap 4551 non-null float64\n",
|
||||
" 6 buyvolume 4551 non-null float64\n",
|
||||
" 7 trades 4551 non-null float64\n",
|
||||
" 8 sellvolume 4551 non-null float64\n",
|
||||
"dtypes: float64(9)\n",
|
||||
"memory usage: 355.5 KB\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"t30data.data[\"BAC\"].info()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"vbt.IF.list_indicators(\"*vwap\")\n",
|
||||
"vbt.phelp(vbt.VWAP.run)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# VWAP"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"\n",
|
||||
"t1vwap_h = vbt.VWAP.run(t1data.high, t1data.low, t1data.close, t1data.volume, anchor=\"H\")\n",
|
||||
"t1vwap_d = vbt.VWAP.run(t1data.high, t1data.low, t1data.close, t1data.volume, anchor=\"D\")\n",
|
||||
"t1vwap_t = vbt.VWAP.run(t1data.high, t1data.low, t1data.close, t1data.volume, anchor=\"T\")\n",
|
||||
"\n",
|
||||
"t1vwap_h_real = t1vwap_h.vwap.vbt.realign_closing(resampler_s)\n",
|
||||
"t1vwap_d_real = t1vwap_d.vwap.vbt.realign_closing(resampler_s)\n",
|
||||
"t1vwap_t_real = t1vwap_t.vwap.vbt.realign_closing(resampler_s)\n",
|
||||
"\n",
|
||||
"#t1vwap_5t.xloc[\"2024-01-3 09:30:00\":\"2024-01-03 16:00:00\"].plot()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#m30data.close.lw.plot()\n",
|
||||
"#quick few liner\n",
|
||||
"pane1 = Panel(\n",
|
||||
" histogram=[\n",
|
||||
" #(s1data.volume, \"volume\",None, 0.8),\n",
|
||||
" #(m30volume, \"m30volume\",None, 1)\n",
|
||||
" ], # [(series, name, \"rgba(53, 94, 59, 0.6)\", opacity)]\n",
|
||||
" right=[\n",
|
||||
" (s1data.close, \"1s close\"),\n",
|
||||
" (t1data.close, \"1min close\"),\n",
|
||||
" (t1vwap_t, \"1mvwap_t\"),\n",
|
||||
" (t1vwap_h, \"1mvwap_h\"),\n",
|
||||
" (t1vwap_d, \"1mvwap_d\"),\n",
|
||||
" (t1vwap_t_real, \"1mvwap_t_real\"),\n",
|
||||
" (t1vwap_h_real, \"1mvwap_h_real\"),\n",
|
||||
" (t1vwap_d_real, \"1mvwap_d_real\")\n",
|
||||
" # (t1close_realigned, \"1min close realigned\"),\n",
|
||||
" # (m30data.close, \"30min-close\"),\n",
|
||||
" # (m30close_realigned, \"30min close realigned\"),\n",
|
||||
" ],\n",
|
||||
")\n",
|
||||
"ch = chart([pane1], size=\"s\", xloc=slice(\"2024-05-1 09:30:00\",\"2024-05-25 16:00:00\"))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# SUPERTREND"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"supertrend_s1 = vbt.SUPERTREND.run(s1data.high, s1data.low, s1data.close, period=5, multiplier=3)\n",
|
||||
"direction_series_s1 = supertrend_s1.direction\n",
|
||||
"supertrend_t1 = vbt.SUPERTREND.run(t1data.high, t1data.low, t1data.close, period=14, multiplier=3)\n",
|
||||
"direction_series_t1 = supertrend_t1.direction\n",
|
||||
"supertrend_t30 = vbt.SUPERTREND.run(t30data.high, t30data.low, t30data.close, period=14, multiplier=3)\n",
|
||||
"direction_series_t30 = supertrend_t30.direction\n",
|
||||
"\n",
|
||||
"resampler_1t_sec = vbt.Resampler(direction_series_t1.index, direction_series_s1.index, source_freq=\"1T\", target_freq=\"1s\")\n",
|
||||
"resampler_30t_sec = vbt.Resampler(direction_series_t30.index, direction_series_s1.index, source_freq=\"30T\", target_freq=\"1s\")\n",
|
||||
"direction_series_t1_realigned = direction_series_t1.vbt.realign_closing(resampler_1t_sec)\n",
|
||||
"direction_series_t30_realigned = direction_series_t30.vbt.realign_closing(resampler_30t_sec)\n",
|
||||
"\n",
|
||||
"#supertrend_s1.xloc[\"2024-01-3 09:30:00\":\"2024-01-03 16:00:00\"].plot()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# aligned_ups= pd.Series(False, index=direction_real.index)\n",
|
||||
"# aligned_downs= pd.Series(False, index=direction_real.index)\n",
|
||||
"\n",
|
||||
"# aligned_ups = direction_real == 1 & supertrend.direction == 1\n",
|
||||
"# aligned_ups"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"s5close = s5data.data[\"BAC\"].close\n",
|
||||
"s5open = s5data.data[\"BAC\"].open\n",
|
||||
"s5high = s5data.data[\"BAC\"].high\n",
|
||||
"s5close_prev = s5close.shift(1)\n",
|
||||
"s5open_prev = s5open.shift(1)\n",
|
||||
"s5high_prev = s5high.shift(1)\n",
|
||||
"#gap nahoru od byci svicky a nevraci se zpet na jeji uroven\n",
|
||||
"entry_ups = (s5close_prev > s5open_prev) & (s5open > s5high_prev + 0.010) & (s5close > s5close_prev)\n",
|
||||
"\n",
|
||||
"entry_ups.value_counts()\n",
|
||||
"\n",
|
||||
"#entry_ups.info()\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Entry window"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"market_open = datetime.time(9, 30)\n",
|
||||
"market_close = datetime.time(16, 0)\n",
|
||||
"entry_window_opens = 10\n",
|
||||
"entry_window_closes = 370"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"entry_window_open= pd.Series(False, index=entry_ups.index)\n",
|
||||
"# Calculate the time difference in minutes from market open for each timestamp\n",
|
||||
"elapsed_min_from_open = (entry_ups.index.hour - market_open.hour) * 60 + (entry_ups.index.minute - market_open.minute)\n",
|
||||
"entry_window_open[(elapsed_min_from_open >= entry_window_opens) & (elapsed_min_from_open < entry_window_closes)] = True\n",
|
||||
"#entry_window_open\n",
|
||||
"\n",
|
||||
"entry_ups = entry_ups & entry_window_open\n",
|
||||
"# entry_ups\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"s5vwap_h = vbt.VWAP.run(s5data.high, s5data.low, s5data.close, s5data.volume, anchor=\"H\")\n",
|
||||
"s5vwap_d = vbt.VWAP.run(s5data.high, s5data.low, s5data.close, s5data.volume, anchor=\"D\")\n",
|
||||
"\n",
|
||||
"# s5vwap_h_real = s5vwap_h.vwap.vbt.realign_closing(resampler_s)\n",
|
||||
"# s5vwap_d_real = s5vwap_d.vwap.vbt.realign_closing(resampler_s)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"pane1 = Panel(\n",
|
||||
" ohlcv=(s5data.data[\"BAC\"],), #(series, entries, exits, other_markers)\n",
|
||||
" histogram=[], # [(series, name, \"rgba(53, 94, 59, 0.6), opacity\")]\n",
|
||||
" right=[#(bbands,), #[(series, name, entries, exits, other_markers)]\n",
|
||||
" (s5data.data[\"BAC\"].close, \"close\", entry_ups),\n",
|
||||
" (s5data.data[\"BAC\"].open, \"open\"),\n",
|
||||
" (s5vwap_h, \"vwap5s_H\",),\n",
|
||||
" (s5vwap_d, \"vwap5s_D\",)\n",
|
||||
" # (t1data.data[\"BAC\"].vwap, \"vwap\"),\n",
|
||||
" # (t1data.close, \"1min close\"),\n",
|
||||
" # (supertrend_s1.trend,\"STtrend\"),\n",
|
||||
" # (supertrend_s1.long,\"STlong\"),\n",
|
||||
" # (supertrend_s1.short,\"STshort\")\n",
|
||||
" ],\n",
|
||||
" left = [\n",
|
||||
" #(direction_series_s1,\"direction_s1\"),\n",
|
||||
" # (direction_series_t1,\"direction_t1\"),\n",
|
||||
" # (direction_series_t30,\"direction_t30\")\n",
|
||||
" \n",
|
||||
" ],\n",
|
||||
" # right=[(bbands.upperband, \"upperband\",),\n",
|
||||
" # (bbands.lowerband, \"lowerband\",),\n",
|
||||
" # (bbands.middleband, \"middleband\",)\n",
|
||||
" # ], #[(series, name, entries, exits, other_markers)]\n",
|
||||
" middle1=[],\n",
|
||||
" middle2=[],\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# pane2 = Panel(\n",
|
||||
"# ohlcv=(t1data.data[\"BAC\"],uptrend_m30, downtrend_m30), #(series, entries, exits, other_markers)\n",
|
||||
"# histogram=[], # [(series, name, \"rgba(53, 94, 59, 0.6), opacity\")]\n",
|
||||
"# left=[#(bbands,), #[(series, name, entries, exits, other_markers)]\n",
|
||||
"# (direction_real,\"direction30min_real\"),\n",
|
||||
"# ],\n",
|
||||
"# # left = [(supertrendm30.direction,\"STdirection30\")],\n",
|
||||
"# # # right=[(bbands.upperband, \"upperband\",),\n",
|
||||
"# # # (bbands.lowerband, \"lowerband\",),\n",
|
||||
"# # # (bbands.middleband, \"middleband\",)\n",
|
||||
"# # # ], #[(series, name, entries, exits, other_markers)]\n",
|
||||
"# middle1=[],\n",
|
||||
"# middle2=[],\n",
|
||||
"# title = \"1m\")\n",
|
||||
"\n",
|
||||
"ch = chart([pane1], sync=True, size=\"s\", xloc=slice(\"2024-02-20 09:30:00\",\"2024-02-22 16:00:00\"), precision=6)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# data = s5data.xloc[\"2024-01-03 09:30:00\":\"2024-03-10 16:00:00\"]\n",
|
||||
"# entry = entry_ups.vbt.xloc[\"2024-01-03 09:30:00\":\"2024-03-10 16:00:00\"].obj\n",
|
||||
"\n",
|
||||
"pf = vbt.Portfolio.from_signals(close=s5data, entries=entry_ups, direction=\"longonly\", sl_stop=0.05/100, tp_stop = 0.05/100, fees=0.0167/100, freq=\"5s\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"pf.xloc[\"2024-01-26 09:30:00\":\"2024-02-28 16:00:00\"].positions.plot()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"pf.xloc[\"2024-01-26 09:30:00\":\"2024-01-28 16:00:00\"].plot()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"pd.set_option('display.max_rows', None)\n",
|
||||
"pf.stats()\n",
|
||||
"# pf.xloc[\"monday\"].stats()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"buyvolume = t1data.data[\"BAC\"].buyvolume\n",
|
||||
"sellvolume = t1data.data[\"BAC\"].sellvolume\n",
|
||||
"totalvolume = buyvolume + sellvolume\n",
|
||||
"\n",
|
||||
"#adjust to minimal value to avoid division by zero\n",
|
||||
"sellvolume_adjusted = sellvolume.replace(0, 1e-10)\n",
|
||||
"oibratio = buyvolume / sellvolume\n",
|
||||
"\n",
|
||||
"#cumulative order flow (net difference)\n",
|
||||
"cof = buyvolume - sellvolume\n",
|
||||
"\n",
|
||||
"# Calculate the order imbalance (net differene) normalize the order imbalance by calculating the difference between buy and sell volumes and then scaling it by the total volume.\n",
|
||||
"order_imbalance = cof / totalvolume\n",
|
||||
"order_imbalance = order_imbalance.fillna(0) #nan nahradime 0\n",
|
||||
"\n",
|
||||
"order_imbalance_allvolume = cof / t1data.data[\"BAC\"].volume\n",
|
||||
"\n",
|
||||
"order_imbalance_sma = vbt.indicator(\"talib:EMA\").run(order_imbalance, timeperiod=5)\n",
|
||||
"short_signals = order_imbalance.vbt < -0.5\n",
|
||||
"#short_entries = oibratio.vbt < 0.01\n",
|
||||
"short_signals.value_counts()\n",
|
||||
"short_signals.name = \"short_entries\"\n",
|
||||
"#.fillna(False)\n",
|
||||
"short_exits = short_signals.shift(-2).fillna(False).astype(bool)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"pane1 = Panel(\n",
|
||||
" ohlcv=(t1data.data[\"BAC\"],), #(series, entries, exits, other_markers)\n",
|
||||
" histogram=[(order_imbalance_allvolume, \"oib_allvolume\", \"rgba(53, 94, 59, 0.6)\",0.5),\n",
|
||||
" (t1data.data[\"BAC\"].trades, \"trades\",None,0.4),\n",
|
||||
" ], # [(series, name, \"rgba(53, 94, 59, 0.6)\", opacity)]\n",
|
||||
" # right=[\n",
|
||||
" # (supertrend.trend,\"STtrend\"),\n",
|
||||
" # (supertrend.long,\"STlong\"),\n",
|
||||
" # (supertrend.short,\"STshort\")\n",
|
||||
" # ],\n",
|
||||
" # left = [(supertrend.direction,\"STdirection\")],\n",
|
||||
" # right=[(bbands.upperband, \"upperband\",),\n",
|
||||
" # (bbands.lowerband, \"lowerband\",),\n",
|
||||
" # (bbands.middleband, \"middleband\",)\n",
|
||||
" # ], #[(series, name, entries, exits, other_markers)]\n",
|
||||
" middle1=[],\n",
|
||||
" middle2=[],\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"pane2 = Panel(\n",
|
||||
" ohlcv=(basic_data.data[\"BAC\"],), #(series, entries, exits, other_markers)\n",
|
||||
" left=[(basic_data.data[\"BAC\"].trades, \"trades\")],\n",
|
||||
" histogram=[(basic_data.data[\"BAC\"].trades, \"trades_hist\", \"white\", 0.5)], #\"rgba(53, 94, 59, 0.6)\"\n",
|
||||
" # ], # [(series, name, \"rgba(53, 94, 59, 0.6)\")]\n",
|
||||
" # right=[\n",
|
||||
" # (supertrend.trend,\"STtrend\"),\n",
|
||||
" # (supertrend.long,\"STlong\"),\n",
|
||||
" # (supertrend.short,\"STshort\")\n",
|
||||
" # ],\n",
|
||||
" # left = [(supertrend.direction,\"STdirection\")],\n",
|
||||
" # right=[(bbands.upperband, \"upperband\",),\n",
|
||||
" # (bbands.lowerband, \"lowerband\",),\n",
|
||||
" # (bbands.middleband, \"middleband\",)\n",
|
||||
" # ], #[(series, name, entries, exits, other_markers)]\n",
|
||||
" middle1=[],\n",
|
||||
" middle2=[],\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"ch = chart([pane1, pane2], size=\"m\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"#short_signal = t1slope.real_below(t1_th) & t2slope.real_below(t2_th) & t3slope.real_below(t3_th) & t4slope.real_below(t4_th)\n",
|
||||
"#long_signal = t1slope.real_above(t1_th) & t2slope.real_above(t2_th) & t3slope.real_above(t3_th) & t4slope.real_above(t4_th)\n",
|
||||
"\n",
|
||||
"#test na daily s reversem crossed 0\n",
|
||||
"short_signal = t2slope.vbt < -0.01 & t3slope.vbt < -0.01 #min value of threshold\n",
|
||||
"long_signal = t2slope.vbt > 0.01 & t3slope.vbt > 0.01 #min\n",
|
||||
"\n",
|
||||
"# thirty_up_signal = t3slope.vbt.crossed_above(0.01)\n",
|
||||
"# thirty_down_signal = t3slope.vbt.crossed_below(-0.01)\n",
|
||||
"\n",
|
||||
"fig = plot_2y_close(priminds=[], secinds=[t3slope], close=t1data.close)\n",
|
||||
"#short_signal.vbt.signals.plot_as_entries(basic_data.close, fig=fig)\n",
|
||||
"\n",
|
||||
"short_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"SHORTS\",\n",
|
||||
" line=dict(color=\"#ffe476\"),\n",
|
||||
" marker=dict(color=\"red\", symbol=\"triangle-down\"),\n",
|
||||
" fill=None,\n",
|
||||
" connectgaps=True,\n",
|
||||
" ))\n",
|
||||
"long_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"LONGS\",\n",
|
||||
" line=dict(color=\"#ffe476\"),\n",
|
||||
" marker=dict(color=\"limegreen\"),\n",
|
||||
" fill=None,\n",
|
||||
" connectgaps=True,\n",
|
||||
" ))\n",
|
||||
"\n",
|
||||
"# thirty_down_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"DOWN30\",\n",
|
||||
"# line=dict(color=\"#ffe476\"),\n",
|
||||
"# marker=dict(color=\"yellow\", symbol=\"triangle-down\"),\n",
|
||||
"# fill=None,\n",
|
||||
"# connectgaps=True,\n",
|
||||
"# ))\n",
|
||||
"# thirty_up_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"UP30\",\n",
|
||||
"# line=dict(color=\"#ffe476\"),\n",
|
||||
"# marker=dict(color=\"grey\"),\n",
|
||||
"# fill=None,\n",
|
||||
"# connectgaps=True,\n",
|
||||
"# ))\n",
|
||||
"\n",
|
||||
"# thirtymin_slope_to_compare.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True), trace_kwargs=dict(name=\"30min slope\",\n",
|
||||
"# line=dict(color=\"yellow\"), \n",
|
||||
"# fill=None,\n",
|
||||
"# connectgaps=True,\n",
|
||||
"# ))\n",
|
||||
"\n",
|
||||
"fig.show()\n",
|
||||
"# print(\"short signal\")\n",
|
||||
"# print(short_signal.value_counts())\n",
|
||||
"\n",
|
||||
"#forced_exit = pd.Series(False, index=close.index)\n",
|
||||
"forced_exit = basic_data.symbol_wrapper.fill(False)\n",
|
||||
"#entry_window_open = pd.Series(False, index=close.index)\n",
|
||||
"entry_window_open= basic_data.symbol_wrapper.fill(False)\n",
|
||||
"\n",
|
||||
"# Calculate the time difference in minutes from market open for each timestamp\n",
|
||||
"elapsed_min_from_open = (forced_exit.index.hour - market_open.hour) * 60 + (forced_exit.index.minute - market_open.minute)\n",
|
||||
"\n",
|
||||
"entry_window_open[(elapsed_min_from_open >= entry_window_opens) & (elapsed_min_from_open < entry_window_closes)] = True\n",
|
||||
"\n",
|
||||
"#print(entry_window_open.value_counts())\n",
|
||||
"\n",
|
||||
"forced_exit[(elapsed_min_from_open >= forced_exit_start) & (elapsed_min_from_open < forced_exit_end)] = True\n",
|
||||
"short_entries = (short_signal & entry_window_open)\n",
|
||||
"short_exits = forced_exit\n",
|
||||
"\n",
|
||||
"entries = (long_signal & entry_window_open)\n",
|
||||
"exits = forced_exit\n",
|
||||
"#long_entries.info()\n",
|
||||
"#number of trues and falses in long_entries\n",
|
||||
"# print(short_exits.value_counts())\n",
|
||||
"# print(short_entries.value_counts())\n",
|
||||
"\n",
|
||||
"#fig = plot_2y_close([],[momshort, rocp], close)\n",
|
||||
"#short_signal.vbt.signals.plot_as_entries(close, fig=fig, add_trace_kwargs=dict(secondary_y=False))\n",
|
||||
"#print(sl_stop)\n",
|
||||
"#short_entries=short_entries, short_exits=short_exits,\n",
|
||||
"# pf = vbt.Portfolio.from_signals(close=basic_data, entries=short_entries, exits=exits, tsl_stop=0.005, tp_stop = 0.05, fees=0.0167/100, freq=\"1s\") #sl_stop=sl_stop, tp_stop = sl_stop,\n",
|
||||
"\n",
|
||||
"# pf.stats()\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"forced_exit = t1data.symbol_wrapper.fill(False)\n",
|
||||
"#entry_window_open = pd.Series(False, index=close.index)\n",
|
||||
"entry_window_open= t1data.symbol_wrapper.fill(False)\n",
|
||||
"\n",
|
||||
"# Calculate the time difference in minutes from market open for each timestamp\n",
|
||||
"elapsed_min_from_open = (forced_exit.index.hour - market_open.hour) * 60 + (forced_exit.index.minute - market_open.minute)\n",
|
||||
"\n",
|
||||
"entry_window_open[(elapsed_min_from_open >= entry_window_opens) & (elapsed_min_from_open < entry_window_closes)] = True\n",
|
||||
"\n",
|
||||
"#print(entry_window_open.value_counts())\n",
|
||||
"\n",
|
||||
"forced_exit[(elapsed_min_from_open >= forced_exit_start) & (elapsed_min_from_open < forced_exit_end)] = True\n",
|
||||
"short_entries = (short_signals & entry_window_open)\n",
|
||||
"short_exits = forced_exit\n",
|
||||
"\n",
|
||||
"entries = (long_signals & entry_window_open)\n",
|
||||
"exits = forced_exit\n",
|
||||
"\n",
|
||||
"pf = vbt.Portfolio.from_signals(close=t1data, entries=entries, exits=exits, short_entries=short_entries, short_exits=exits,\n",
|
||||
"td_stop=2, time_delta_format=\"rows\",\n",
|
||||
"tsl_stop=0.005, tp_stop = 0.005, fees=0.0167/100)#, freq=\"1s\") #sl_stop=sl_stop, tp_stop = sl_stop,\n",
|
||||
"\n",
|
||||
"pf.stats()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"pf.plot()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"pf.get_drawdowns().records_readable"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"pf.orders.records_readable"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": ".venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.11"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
API_KEY = 'PKGGEWIEYZOVQFDRY70L'
|
||||
SECRET_KEY = 'O5Kt8X4RLceIOvM98i5LdbalItsX7hVZlbPYHy8Y'
|
||||
API_KEY = ''
|
||||
SECRET_KEY = ''
|
||||
MAX_BATCH_SIZE = 1
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import numpy as np
|
||||
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
||||
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
|
||||
from typing import Tuple
|
||||
from copy import deepcopy
|
||||
from v2realbot.strategy.base import StrategyState
|
||||
from v2realbot.strategyblocks.activetrade.helpers import get_max_profit_price, get_profit_target_price, get_override_for_active_trade, keyword_conditions_met
|
||||
from v2realbot.strategyblocks.activetrade.helpers import get_max_profit_price, get_profit_target_price, get_signal_section_directive, keyword_conditions_met
|
||||
from v2realbot.utils.utils import safe_get
|
||||
# FIBONACCI PRO PROFIT A SL
|
||||
|
||||
@@ -63,10 +63,10 @@ class SLOptimizer:
|
||||
|
||||
def initialize_levels(self, state):
|
||||
directive_name = 'SL_opt_exit_levels_'+str(self.direction)
|
||||
SL_opt_exit_levels = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
||||
SL_opt_exit_levels = get_signal_section_directive(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
||||
|
||||
directive_name = 'SL_opt_exit_sizes_'+str(self.direction)
|
||||
SL_opt_exit_sizes = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
||||
SL_opt_exit_sizes = get_signal_section_directive(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
||||
|
||||
if SL_opt_exit_levels is None or SL_opt_exit_sizes is not None:
|
||||
print("no directives found: SL_opt_exit_levels/SL_opt_exit_sizes")
|
||||
|
||||
2
testy/testtt.py
Normal file
2
testy/testtt.py
Normal file
@@ -0,0 +1,2 @@
|
||||
import locale
|
||||
print(locale.getdefaultlocale())
|
||||
@@ -59,25 +59,12 @@ Hlavní loop:
|
||||
|
||||
"""
|
||||
def next(data, state: StrategyState):
|
||||
##print(10*"*","NEXT START",10*"*")
|
||||
# important vars state.avgp, state.positions, state.vars, data
|
||||
|
||||
#indicators moved to call_next in upper class
|
||||
print(10*"*", state.account_variables)
|
||||
|
||||
#pokud mame prazdne pozice a neceka se na nic
|
||||
if state.positions == 0 and state.vars.pending is None:
|
||||
#vykoname trady ve fronte
|
||||
execute_prescribed_trades(state, data)
|
||||
#pokud se neaktivoval nejaky trade, poustime signal search - ale jen jednou za bar?
|
||||
#if conf_bar == 1:
|
||||
if state.vars.pending is None:
|
||||
signal_search(state, data)
|
||||
#pro jistotu ihned zpracujeme
|
||||
execute_prescribed_trades(state, data)
|
||||
|
||||
#mame aktivni trade a neceka se n anic
|
||||
elif state.vars.activeTrade and state.vars.pending is None:
|
||||
manage_active_trade(state, data)
|
||||
execute_prescribed_trades(state, data)
|
||||
signal_search(state, data)
|
||||
execute_prescribed_trades(state, data) #pro jistotu ihned zpracujeme
|
||||
manage_active_trade(state, data)
|
||||
|
||||
def init(state: StrategyState):
|
||||
#place to declare new vars
|
||||
@@ -88,13 +75,13 @@ def init(state: StrategyState):
|
||||
|
||||
#nove atributy na rizeni tradu
|
||||
#identifikuje provedenou změnu na Tradu (neděláme změny dokud nepřijde potvrzeni z notifikace)
|
||||
state.vars.pending = None
|
||||
#state.vars.pending = None #nahrazeno pebnding pod accountem state.account_variables[account.name].pending
|
||||
#obsahuje aktivni Trade a jeho nastaveni
|
||||
state.vars.activeTrade = None #pending/Trade
|
||||
#state.vars.activeTrade = None #pending/Trade moved to account_variables
|
||||
#obsahuje pripravene Trady ve frontě
|
||||
state.vars.prescribedTrades = []
|
||||
#flag pro reversal
|
||||
state.vars.requested_followup = None
|
||||
#state.vars.requested_followup = None #nahrazeno pod accountem
|
||||
|
||||
#TODO presunout inicializaci work_dict u podminek - sice hodnoty nepujdou zmenit, ale zlepsi se performance
|
||||
#pripadne udelat refresh kazdych x-iterací
|
||||
@@ -102,9 +89,8 @@ def init(state: StrategyState):
|
||||
state.vars.mode = None
|
||||
state.vars.last_50_deltas = []
|
||||
state.vars.next_new = 0
|
||||
state.vars.last_buy_index = None
|
||||
state.vars.last_exit_index = None
|
||||
state.vars.last_in_index = None
|
||||
state.vars.last_entry_index = None #mponechano obecne pro vsechny accounty
|
||||
state.vars.last_exit_index = None #obecna varianta ponechana
|
||||
state.vars.last_update_time = 0
|
||||
state.vars.reverse_position_waiting_amount = 0
|
||||
#INIT promenne, ktere byly zbytecne ve stratvars
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"""
|
||||
from uuid import UUID, uuid4
|
||||
from alpaca.trading.enums import OrderSide, OrderStatus, TradeEvent, OrderType
|
||||
from v2realbot.common.model import TradeUpdate, Order
|
||||
from v2realbot.common.model import TradeUpdate, Order, Account
|
||||
from rich import print as printanyway
|
||||
import threading
|
||||
import asyncio
|
||||
@@ -61,6 +61,7 @@ import dash_bootstrap_components as dbc
|
||||
from dash.dependencies import Input, Output
|
||||
from dash import dcc, html, dash_table, Dash
|
||||
import v2realbot.utils.config_handler as cfh
|
||||
from typing import Set
|
||||
""""
|
||||
LATENCY DELAYS
|
||||
.000 trigger - last_trade_time (.4246266)
|
||||
@@ -74,7 +75,20 @@ lock = threading.Lock
|
||||
#todo nejspis dat do classes, aby se mohlo backtestovat paralelne
|
||||
#ted je globalni promena last_time_now a self.account a cash
|
||||
class Backtester:
|
||||
def __init__(self, symbol: str, order_fill_callback: callable, btdata: list, bp_from: datetime, bp_to: datetime, cash: float = 100000):
|
||||
"""
|
||||
Initializes a new instance of the Backtester class.
|
||||
Args:
|
||||
symbol (str): The symbol of the security being backtested.
|
||||
accounts (set): A set of accounts to use for backtesting.
|
||||
order_fill_callback (callable): A callback function to handle order fills.
|
||||
btdata (list): A list of backtesting data.
|
||||
bp_from (datetime): The start date of the backtesting period.
|
||||
bp_to (datetime): The end date of the backtesting period.
|
||||
cash (float, optional): The initial cash balance. Defaults to 100000.
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
def __init__(self, symbol: str, accounts: Set, order_fill_callback: callable, btdata: list, bp_from: datetime, bp_to: datetime, cash: float = 100000):
|
||||
#this TIME value determines true time for submit, replace, cancel order should happen (allowing past)
|
||||
#it is set by every iteration of BT or before fill callback - allowing past events to happen
|
||||
self.time = None
|
||||
@@ -83,6 +97,7 @@ class Backtester:
|
||||
self.btdata = btdata
|
||||
self.backtest_start = None
|
||||
self.backtest_end = None
|
||||
self.accounts = accounts
|
||||
self.cash_init = cash
|
||||
#backtesting period
|
||||
self.bp_from = bp_from
|
||||
@@ -90,9 +105,10 @@ class Backtester:
|
||||
self.cash = cash
|
||||
self.cash_reserved_for_shorting = 0
|
||||
self.trades = []
|
||||
self.account = { "BAC": [0, 0] }
|
||||
# { "BAC": [avgp, size] }
|
||||
self.open_orders =[]
|
||||
self.internal_account = { account.name:{self.symbol: [0, 0]} for account in accounts }
|
||||
# { "ACCOUNT1": {}"BAC": [avgp, size]}, .... }
|
||||
self.open_orders =[] #open orders shared for all accounts, account being an attribute
|
||||
|
||||
# self.open_orders = [Order(id=uuid4(),
|
||||
# submitted_at = datetime(2023, 3, 17, 9, 30, 0, 0, tzinfo=zoneNY),
|
||||
# symbol = "BAC",
|
||||
@@ -110,6 +126,8 @@ class Backtester:
|
||||
# side = OrderSide.BUY)]
|
||||
|
||||
#
|
||||
|
||||
|
||||
def execute_orders_and_callbacks(self, intime: float):
|
||||
"""""
|
||||
Voláno ze strategie před každou iterací s časem T.
|
||||
@@ -166,7 +184,7 @@ class Backtester:
|
||||
|
||||
for order in self.open_orders:
|
||||
#pokud je vyplneny symbol, tak jedeme jen tyto, jinak vsechny
|
||||
print(order.id, datetime.timestamp(order.submitted_at), order.symbol, order.side, order.order_type, order.qty, order.limit_price, order.submitted_at)
|
||||
print(order.account.name, order.id, datetime.timestamp(order.submitted_at), order.symbol, order.side, order.order_type, order.qty, order.limit_price, order.submitted_at)
|
||||
if order.canceled_at:
|
||||
#ic("deleting canceled order",order.id)
|
||||
todel.append(order)
|
||||
@@ -348,21 +366,22 @@ class Backtester:
|
||||
|
||||
#ic(o.filled_at, o.filled_avg_price)
|
||||
|
||||
a = self.update_account(o = o)
|
||||
a = self.update_internal_account(o = o)
|
||||
if a < 0:
|
||||
tlog("update_account ERROR")
|
||||
return -1
|
||||
|
||||
trade = TradeUpdate(order = o,
|
||||
trade = TradeUpdate(account=o.account,
|
||||
order = o,
|
||||
event = TradeEvent.FILL,
|
||||
execution_id = str(uuid4()),
|
||||
timestamp = datetime.fromtimestamp(fill_time),
|
||||
position_qty= self.account[o.symbol][0],
|
||||
position_qty= self.internal_account[o.account.name][o.symbol][0],
|
||||
price=float(fill_price),
|
||||
qty = o.qty,
|
||||
value = float(o.qty*fill_price),
|
||||
cash = self.cash,
|
||||
pos_avg_price = self.account[o.symbol][1])
|
||||
pos_avg_price = self.internal_account[o.account.name][o.symbol][1])
|
||||
|
||||
self.trades.append(trade)
|
||||
|
||||
@@ -379,49 +398,49 @@ class Backtester:
|
||||
self.time = time + float(cfh.config_handler.get_val('BT_DELAYS','fill_to_not'))
|
||||
print("current bt.time",self.time)
|
||||
#print("FILL NOTIFICATION: ", tradeupdate)
|
||||
res = asyncio.run(self.order_fill_callback(tradeupdate))
|
||||
res = asyncio.run(self.order_fill_callback(tradeupdate, tradeupdate.account))
|
||||
return 0
|
||||
|
||||
def update_account(self, o: Order):
|
||||
def update_internal_account(self, o: Order):
|
||||
#updatujeme self.account
|
||||
#pokud neexistuje klic v accountu vytvorime si ho
|
||||
if o.symbol not in self.account:
|
||||
if o.symbol not in self.internal_account[o.account.name]:
|
||||
# { "BAC": [size, avgp] }
|
||||
self.account[o.symbol] = [0,0]
|
||||
self.internal_account[o.account.name][o.symbol] = [0,0]
|
||||
|
||||
if o.side == OrderSide.BUY:
|
||||
#[size, avgp]
|
||||
newsize = (self.account[o.symbol][0] + o.qty)
|
||||
newsize = (self.internal_account[o.account.name][o.symbol][0] + o.qty)
|
||||
#JPLNE UZAVRENI SHORT (avgp 0)
|
||||
if newsize == 0: newavgp = 0
|
||||
#CASTECNE UZAVRENI SHORT (avgp puvodni)
|
||||
elif newsize < 0: newavgp = self.account[o.symbol][1]
|
||||
elif newsize < 0: newavgp = self.internal_account[o.account.name][o.symbol][1]
|
||||
#JDE O LONG (avgp nove)
|
||||
else:
|
||||
newavgp = ((self.account[o.symbol][0] * self.account[o.symbol][1]) + (o.qty * o.filled_avg_price)) / (self.account[o.symbol][0] + o.qty)
|
||||
newavgp = ((self.internal_account[o.account.name][o.symbol][0] * self.internal_account[o.account.name][o.symbol][1]) + (o.qty * o.filled_avg_price)) / (self.internal_account[o.account.name][o.symbol][0] + o.qty)
|
||||
|
||||
self.account[o.symbol] = [newsize, newavgp]
|
||||
self.internal_account[o.account.name][o.symbol] = [newsize, newavgp]
|
||||
self.cash = self.cash - (o.qty * o.filled_avg_price)
|
||||
return 1
|
||||
#sell
|
||||
elif o.side == OrderSide.SELL:
|
||||
newsize = self.account[o.symbol][0]-o.qty
|
||||
newsize = self.internal_account[o.account.name][o.symbol][0]-o.qty
|
||||
#UPLNE UZAVRENI LONGU (avgp 0)
|
||||
if newsize == 0: newavgp = 0
|
||||
#CASTECNE UZAVRENI LONGU (avgp puvodni)
|
||||
elif newsize > 0: newavgp = self.account[o.symbol][1]
|
||||
elif newsize > 0: newavgp = self.internal_account[o.account.name][o.symbol][1]
|
||||
#jde o SHORT (avgp nove)
|
||||
else:
|
||||
#pokud je predchozi 0 - tzn. jde o prvni short
|
||||
if self.account[o.symbol][1] == 0:
|
||||
if self.internal_account[o.account.name][o.symbol][1] == 0:
|
||||
newavgp = o.filled_avg_price
|
||||
else:
|
||||
newavgp = ((abs(self.account[o.symbol][0]) * self.account[o.symbol][1]) + (o.qty * o.filled_avg_price)) / (abs(self.account[o.symbol][0]) + o.qty)
|
||||
newavgp = ((abs(self.internal_account[o.account.name][o.symbol][0]) * self.internal_account[o.account.name][o.symbol][1]) + (o.qty * o.filled_avg_price)) / (abs(self.internal_account[o.account.name][o.symbol][0]) + o.qty)
|
||||
|
||||
self.account[o.symbol] = [newsize, newavgp]
|
||||
self.internal_account[o.account.name][o.symbol] = [newsize, newavgp]
|
||||
|
||||
#pokud jde o prodej longu(nova pozice je>=0) upravujeme cash
|
||||
if self.account[o.symbol][0] >= 0:
|
||||
if self.internal_account[o.account.name][o.symbol][0] >= 0:
|
||||
self.cash = float(self.cash + (o.qty * o.filled_avg_price))
|
||||
print("uprava cashe, jde o prodej longu")
|
||||
else:
|
||||
@@ -466,7 +485,7 @@ class Backtester:
|
||||
# #ic("get last price")
|
||||
# return self.btdata[i-1][1]
|
||||
|
||||
def submit_order(self, time: float, symbol: str, side: OrderSide, size: int, order_type: OrderType, price: float = None):
|
||||
def submit_order(self, time: float, symbol: str, side: OrderSide, size: int, order_type: OrderType, account: Account, price: float = None):
|
||||
"""submit order
|
||||
- zakladni validace
|
||||
- vloží do self.open_orders s daným časem
|
||||
@@ -499,9 +518,9 @@ class Backtester:
|
||||
return -1
|
||||
|
||||
#pokud neexistuje klic v accountu vytvorime si ho
|
||||
if symbol not in self.account:
|
||||
if symbol not in self.internal_account[account.name]:
|
||||
# { "BAC": [size, avgp] }
|
||||
self.account[symbol] = [0,0]
|
||||
self.internal_account[account.name][symbol] = [0,0]
|
||||
|
||||
#check for available quantity
|
||||
if side == OrderSide.SELL:
|
||||
@@ -509,15 +528,15 @@ class Backtester:
|
||||
reserved_price = 0
|
||||
#with lock:
|
||||
for o in self.open_orders:
|
||||
if o.side == OrderSide.SELL and o.symbol == symbol and o.canceled_at is None:
|
||||
if o.side == OrderSide.SELL and o.symbol == symbol and o.canceled_at is None and o.account==account:
|
||||
reserved += o.qty
|
||||
cena = o.limit_price if o.limit_price else self.get_last_price(time, o.symbol)
|
||||
reserved_price += o.qty * cena
|
||||
print("blokovano v open orders pro sell qty: ", reserved, "celkem:", reserved_price)
|
||||
|
||||
actual_minus_reserved = int(self.account[symbol][0]) - reserved
|
||||
actual_minus_reserved = int(self.internal_account[account.name][symbol][0]) - reserved
|
||||
if actual_minus_reserved > 0 and actual_minus_reserved - int(size) < 0:
|
||||
printanyway("not enough shares available to sell or shorting while long position",self.account[symbol][0],"reserved",reserved,"available",int(self.account[symbol][0]) - reserved,"selling",size)
|
||||
printanyway("not enough shares available to sell or shorting while long position",self.internal_account[account.name][symbol][0],"reserved",reserved,"available",int(self.internal_account[account.name][symbol][0]) - reserved,"selling",size)
|
||||
return -1
|
||||
|
||||
#if is shorting - check available cash to short
|
||||
@@ -533,13 +552,13 @@ class Backtester:
|
||||
reserved_price = 0
|
||||
#with lock:
|
||||
for o in self.open_orders:
|
||||
if o.side == OrderSide.BUY and o.canceled_at is None:
|
||||
if o.side == OrderSide.BUY and o.canceled_at is None and o.account==account:
|
||||
cena = o.limit_price if o.limit_price else self.get_last_price(time, o.symbol)
|
||||
reserved_price += o.qty * cena
|
||||
reserved_qty += o.qty
|
||||
print("blokovano v open orders for buy: qty, price", reserved_qty, reserved_price)
|
||||
|
||||
actual_plus_reserved_qty = int(self.account[symbol][0]) + reserved_qty
|
||||
actual_plus_reserved_qty = int(self.internal_account[account.name][symbol][0]) + reserved_qty
|
||||
|
||||
#jde o uzavreni shortu
|
||||
if actual_plus_reserved_qty < 0 and (actual_plus_reserved_qty + int(size)) > 0:
|
||||
@@ -555,6 +574,7 @@ class Backtester:
|
||||
|
||||
id = str(uuid4())
|
||||
order = Order(id=id,
|
||||
account=account,
|
||||
submitted_at = datetime.fromtimestamp(float(time)),
|
||||
symbol=symbol,
|
||||
order_type = order_type,
|
||||
@@ -569,7 +589,7 @@ class Backtester:
|
||||
return id
|
||||
|
||||
|
||||
def replace_order(self, id: str, time: float, size: int = None, price: float = None):
|
||||
def replace_order(self, id: str, time: float, account: Account, size: int = None, price: float = None):
|
||||
"""replace order
|
||||
- zakladni validace vrací synchronně
|
||||
- vrací číslo nové objednávky
|
||||
@@ -586,7 +606,7 @@ class Backtester:
|
||||
#with lock:
|
||||
for o in self.open_orders:
|
||||
print(o.id)
|
||||
if str(o.id) == str(id):
|
||||
if str(o.id) == str(id) and o.account == account:
|
||||
newid = str(uuid4())
|
||||
o.id = newid
|
||||
o.submitted_at = datetime.fromtimestamp(time)
|
||||
@@ -597,7 +617,7 @@ class Backtester:
|
||||
print("BT: replacement order doesnt exist")
|
||||
return 0
|
||||
|
||||
def cancel_order(self, time: float, id: str):
|
||||
def cancel_order(self, time: float, id: str, account: Account):
|
||||
"""cancel order
|
||||
- základní validace vrací synchronně
|
||||
- vymaže objednávku z open orders
|
||||
@@ -613,22 +633,22 @@ class Backtester:
|
||||
return 0
|
||||
#with lock:
|
||||
for o in self.open_orders:
|
||||
if str(o.id) == id:
|
||||
if str(o.id) == id and o.account == account:
|
||||
o.canceled_at = time
|
||||
print("set as canceled in self.open_orders")
|
||||
return 1
|
||||
print("BTC: cantchange. open order doesnt exist")
|
||||
return 0
|
||||
|
||||
def get_open_position(self, symbol: str):
|
||||
def get_open_position(self, symbol: str, account: Account):
|
||||
"""get positions ->(avg,size)"""
|
||||
#print("BT:get open positions entry")
|
||||
try:
|
||||
return self.account[symbol][1], self.account[symbol][0]
|
||||
return self.internal_account[account.name][symbol][1], self.internal_account[account.name][symbol][0]
|
||||
except:
|
||||
return (0,0)
|
||||
|
||||
def get_open_orders(self, side: OrderSide, symbol: str):
|
||||
def get_open_orders(self, side: OrderSide, symbol: str, account: Account):
|
||||
"""get open orders ->list(OrderNotification)"""
|
||||
print("BT:get open orders entry")
|
||||
if len(self.open_orders) == 0:
|
||||
@@ -638,7 +658,7 @@ class Backtester:
|
||||
#with lock:
|
||||
for o in self.open_orders:
|
||||
#print(o)
|
||||
if o.symbol == symbol and o.canceled_at is None:
|
||||
if o.symbol == symbol and o.canceled_at is None and o.account == account:
|
||||
if side is None or o.side == side:
|
||||
res.append(o)
|
||||
return res
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
from enum import Enum
|
||||
from datetime import datetime
|
||||
from pydantic import BaseModel
|
||||
from typing import Any, Optional, List, Union
|
||||
from uuid import UUID
|
||||
class TradeStatus(str, Enum):
|
||||
READY = "ready"
|
||||
ACTIVATED = "activated"
|
||||
CLOSED = "closed"
|
||||
#FINISHED = "finished"
|
||||
|
||||
class TradeDirection(str, Enum):
|
||||
LONG = "long"
|
||||
SHORT = "short"
|
||||
|
||||
class TradeStoplossType(str, Enum):
|
||||
FIXED = "fixed"
|
||||
TRAILING = "trailing"
|
||||
|
||||
#Predpis obchodu vygenerovany signalem, je to zastresujici jednotka
|
||||
#ke kteremu jsou pak navazany jednotlivy FILLy (reprezentovany model.TradeUpdate) - napr. castecne exity atp.
|
||||
class Trade(BaseModel):
|
||||
id: UUID
|
||||
last_update: datetime
|
||||
entry_time: Optional[datetime] = None
|
||||
exit_time: Optional[datetime] = None
|
||||
status: TradeStatus
|
||||
generated_by: Optional[str] = None
|
||||
direction: TradeDirection
|
||||
entry_price: Optional[float] = None
|
||||
goal_price: Optional[float] = None
|
||||
size: Optional[int] = None
|
||||
# size_multiplier je pomocna promenna pro pocitani relativniho denniho profit
|
||||
size_multiplier: Optional[float] = None
|
||||
# stoploss_type: TradeStoplossType
|
||||
stoploss_value: Optional[float] = None
|
||||
profit: Optional[float] = 0
|
||||
profit_sum: Optional[float] = 0
|
||||
rel_profit: Optional[float] = 0
|
||||
rel_profit_cum: Optional[float] = 0
|
||||
|
||||
@@ -5,10 +5,75 @@ from rich import print
|
||||
from typing import Any, Optional, List, Union
|
||||
from datetime import datetime, date
|
||||
from pydantic import BaseModel, Field
|
||||
from v2realbot.enums.enums import Mode, Account, SchedulerStatus, Moddus, Market
|
||||
from v2realbot.enums.enums import Mode, Account, SchedulerStatus, Moddus, Market, Followup
|
||||
from alpaca.data.enums import Exchange
|
||||
from enum import Enum
|
||||
from datetime import datetime
|
||||
from pydantic import BaseModel
|
||||
from typing import Any, Optional, List, Union
|
||||
from uuid import UUID
|
||||
|
||||
|
||||
#prescribed model
|
||||
#from prescribed model
|
||||
class InstantIndicator(BaseModel):
|
||||
name: str
|
||||
toml: str
|
||||
|
||||
|
||||
class TradeStatus(str, Enum):
|
||||
READY = "ready"
|
||||
ACTIVATED = "activated"
|
||||
CLOSED = "closed"
|
||||
#FINISHED = "finished"
|
||||
|
||||
class TradeDirection(str, Enum):
|
||||
LONG = "long"
|
||||
SHORT = "short"
|
||||
|
||||
class TradeStoplossType(str, Enum):
|
||||
FIXED = "fixed"
|
||||
TRAILING = "trailing"
|
||||
|
||||
#Predpis obchodu vygenerovany signalem, je to zastresujici jednotka
|
||||
#ke kteremu jsou pak navazany jednotlivy FILLy (reprezentovany model.TradeUpdate) - napr. castecne exity atp.
|
||||
class Trade(BaseModel):
|
||||
account: Account
|
||||
id: UUID
|
||||
last_update: datetime
|
||||
entry_time: Optional[datetime] = None
|
||||
exit_time: Optional[datetime] = None
|
||||
status: TradeStatus
|
||||
generated_by: Optional[str] = None
|
||||
direction: TradeDirection
|
||||
entry_price: Optional[float] = None
|
||||
goal_price: Optional[float] = None
|
||||
size: Optional[int] = None
|
||||
# size_multiplier je pomocna promenna pro pocitani relativniho denniho profit
|
||||
size_multiplier: Optional[float] = None
|
||||
# stoploss_type: TradeStoplossType
|
||||
stoploss_value: Optional[float] = None
|
||||
profit: Optional[float] = 0
|
||||
profit_sum: Optional[float] = 0
|
||||
rel_profit: Optional[float] = 0
|
||||
rel_profit_cum: Optional[float] = 0
|
||||
|
||||
#account variables that can be accessed by ACCOUNT key dictionary
|
||||
class AccountVariables(BaseModel):
|
||||
positions: float = 0
|
||||
avgp: float = 0
|
||||
pending: str = None
|
||||
blockbuy: int = 0
|
||||
wait_for_fill: float = None
|
||||
profit: float = 0
|
||||
docasny_rel_profit: list = []
|
||||
rel_profit_cum: list = []
|
||||
last_entry_index: int = None #acc varianta, mame taky obnecnou state.vars.last_entry_index
|
||||
requested_followup: Followup = None
|
||||
activeTrade: Trade = None
|
||||
dont_exit_already_activated: bool = False
|
||||
#activeTrade, prescribedTrades
|
||||
#tbd transferables?
|
||||
|
||||
|
||||
#models for server side datatables
|
||||
@@ -91,7 +156,7 @@ class TestList(BaseModel):
|
||||
dates: List[Intervals]
|
||||
|
||||
#for GUI to fetch historical trades on given symbol
|
||||
class Trade(BaseModel):
|
||||
class TradeView(BaseModel):
|
||||
symbol: str
|
||||
timestamp: datetime
|
||||
exchange: Optional[Union[Exchange, str]] = None
|
||||
@@ -189,8 +254,8 @@ class RunnerView(BaseModel):
|
||||
run_symbol: Optional[str] = None
|
||||
run_trade_count: Optional[int] = 0
|
||||
run_profit: Optional[float] = 0
|
||||
run_positions: Optional[int] = 0
|
||||
run_avgp: Optional[float] = 0
|
||||
run_positions: Optional[dict] = 0
|
||||
run_avgp: Optional[dict] = 0
|
||||
run_stopped: Optional[datetime] = None
|
||||
run_paused: Optional[datetime] = None
|
||||
|
||||
@@ -208,8 +273,8 @@ class Runner(BaseModel):
|
||||
run_ilog_save: Optional[bool] = False
|
||||
run_trade_count: Optional[int] = None
|
||||
run_profit: Optional[float] = None
|
||||
run_positions: Optional[int] = None
|
||||
run_avgp: Optional[float] = None
|
||||
run_positions: Optional[dict] = None
|
||||
run_avgp: Optional[dict] = None
|
||||
run_strat_json: Optional[str] = None
|
||||
run_stopped: Optional[datetime] = None
|
||||
run_paused: Optional[datetime] = None
|
||||
@@ -247,6 +312,7 @@ class Bar(BaseModel):
|
||||
vwap: Optional[float] = 0
|
||||
|
||||
class Order(BaseModel):
|
||||
account: Account
|
||||
id: UUID
|
||||
submitted_at: datetime
|
||||
filled_at: Optional[datetime] = None
|
||||
@@ -262,6 +328,7 @@ class Order(BaseModel):
|
||||
|
||||
#entita pro kazdy kompletni FILL, je navazana na prescribed_trade
|
||||
class TradeUpdate(BaseModel):
|
||||
account: Account
|
||||
event: Union[TradeEvent, str]
|
||||
execution_id: Optional[UUID] = None
|
||||
order: Order
|
||||
@@ -307,8 +374,8 @@ class RunArchive(BaseModel):
|
||||
ilog_save: Optional[bool] = False
|
||||
profit: float = 0
|
||||
trade_count: int = 0
|
||||
end_positions: int = 0
|
||||
end_positions_avgp: float = 0
|
||||
end_positions: Union[dict,str] = None
|
||||
end_positions_avgp: Union[dict,str] = None
|
||||
metrics: Union[dict, str] = None
|
||||
stratvars_toml: Optional[str] = None
|
||||
|
||||
@@ -329,8 +396,8 @@ class RunArchiveView(BaseModel):
|
||||
ilog_save: Optional[bool] = False
|
||||
profit: float = 0
|
||||
trade_count: int = 0
|
||||
end_positions: int = 0
|
||||
end_positions_avgp: float = 0
|
||||
end_positions: Union[dict,int] = None
|
||||
end_positions_avgp: Union[dict,float] = None
|
||||
metrics: Union[dict, str] = None
|
||||
batch_profit: float = 0 # Total profit for the batch - now calculated during query
|
||||
batch_count: int = 0 # Count of runs in the batch - now calculated during query
|
||||
@@ -347,6 +414,8 @@ class SLHistory(BaseModel):
|
||||
id: Optional[UUID] = None
|
||||
time: datetime
|
||||
sl_val: float
|
||||
direction: TradeDirection
|
||||
account: Account
|
||||
|
||||
#Contains archive of running strategies (runner) - detail data
|
||||
class RunArchiveDetail(BaseModel):
|
||||
@@ -359,9 +428,3 @@ class RunArchiveDetail(BaseModel):
|
||||
trades: List[TradeUpdate]
|
||||
ext_data: Optional[dict] = None
|
||||
|
||||
|
||||
class InstantIndicator(BaseModel):
|
||||
name: str
|
||||
toml: str
|
||||
|
||||
|
||||
|
||||
@@ -51,8 +51,8 @@ def row_to_runarchiveview(row: dict) -> RunArchiveView:
|
||||
ilog_save=bool(row['ilog_save']),
|
||||
profit=float(row['profit']),
|
||||
trade_count=int(row['trade_count']),
|
||||
end_positions=int(row['end_positions']),
|
||||
end_positions_avgp=float(row['end_positions_avgp']),
|
||||
end_positions=orjson.loads(row['end_positions']),
|
||||
end_positions_avgp=orjson.loads(row['end_positions_avgp']),
|
||||
metrics=orjson.loads(row['metrics']) if row['metrics'] else None,
|
||||
batch_profit=int(row['batch_profit']) if row['batch_profit'] and row['batch_id'] else 0,
|
||||
batch_count=int(row['batch_count']) if row['batch_count'] and row['batch_id'] else 0,
|
||||
@@ -79,8 +79,8 @@ def row_to_runarchive(row: dict) -> RunArchive:
|
||||
ilog_save=bool(row['ilog_save']),
|
||||
profit=float(row['profit']),
|
||||
trade_count=int(row['trade_count']),
|
||||
end_positions=int(row['end_positions']),
|
||||
end_positions_avgp=float(row['end_positions_avgp']),
|
||||
end_positions=str(row['end_positions']),
|
||||
end_positions_avgp=str(row['end_positions_avgp']),
|
||||
metrics=orjson.loads(row['metrics']),
|
||||
stratvars_toml=row['stratvars_toml'],
|
||||
transferables=orjson.loads(row['transferables']) if row['transferables'] else None
|
||||
|
||||
@@ -17,9 +17,6 @@ RUNNER_DETAIL_DIRECTORY = Path(__file__).parent.parent.parent / "runner_detail"
|
||||
LOG_PATH = Path(__file__).parent.parent
|
||||
LOG_FILE = Path(__file__).parent.parent / "strat.log"
|
||||
JOB_LOG_FILE = Path(__file__).parent.parent / "job.log"
|
||||
DOTENV_DIRECTORY = Path(__file__).parent.parent.parent
|
||||
ENV_FILE = DOTENV_DIRECTORY / '.env'
|
||||
|
||||
|
||||
#stratvars that cannot be changed in gui
|
||||
STRATVARS_UNCHANGEABLES = ['pendingbuys', 'blockbuy', 'jevylozeno', 'limitka']
|
||||
@@ -30,6 +27,26 @@ MODEL_DIR = Path(DATA_DIR)/"models"
|
||||
PROFILING_NEXT_ENABLED = False
|
||||
PROFILING_OUTPUT_DIR = DATA_DIR
|
||||
|
||||
def find_dotenv(start_path):
|
||||
"""
|
||||
Searches for a .env file in the given directory or its parents and returns the path.
|
||||
|
||||
Args:
|
||||
start_path: The directory to start searching from.
|
||||
|
||||
Returns:
|
||||
Path to the .env file if found, otherwise None.
|
||||
"""
|
||||
current_path = Path(start_path)
|
||||
for _ in range(6): # Limit search depth to 5 levels
|
||||
dotenv_path = current_path / '.env'
|
||||
if dotenv_path.exists():
|
||||
return dotenv_path
|
||||
current_path = current_path.parent
|
||||
return None
|
||||
|
||||
ENV_FILE = find_dotenv(__file__)
|
||||
|
||||
#NALOADUJEME DOTENV ENV VARIABLES
|
||||
if load_dotenv(ENV_FILE, verbose=True) is False:
|
||||
print(f"Error loading.env file {ENV_FILE}. Now depending on ENV VARIABLES set externally.")
|
||||
@@ -66,10 +83,10 @@ def get_key(mode: Mode, account: Account):
|
||||
return None
|
||||
dict = globals()
|
||||
try:
|
||||
API_KEY = dict[str.upper(str(account.value)) + "_" + str.upper(str(mode.value)) + "_API_KEY" ]
|
||||
SECRET_KEY = dict[str.upper(str(account.value)) + "_" + str.upper(str(mode.value)) + "_SECRET_KEY" ]
|
||||
PAPER = dict[str.upper(str(account.value)) + "_" + str.upper(str(mode.value)) + "_PAPER" ]
|
||||
FEED = dict[str.upper(str(account.value)) + "_" + str.upper(str(mode.value)) + "_FEED" ]
|
||||
API_KEY = dict[str.upper(str(account.name)) + "_" + str.upper(str(mode.name)) + "_API_KEY" ]
|
||||
SECRET_KEY = dict[str.upper(str(account.name)) + "_" + str.upper(str(mode.name)) + "_SECRET_KEY" ]
|
||||
PAPER = dict[str.upper(str(account.name)) + "_" + str.upper(str(mode.name)) + "_PAPER" ]
|
||||
FEED = dict[str.upper(str(account.name)) + "_" + str.upper(str(mode.name)) + "_FEED" ]
|
||||
return Keys(API_KEY, SECRET_KEY, PAPER, FEED)
|
||||
except KeyError:
|
||||
print("Not valid combination to get keys for", mode, account)
|
||||
@@ -93,7 +110,7 @@ data_feed_type_str = os.environ.get('ACCOUNT1_PAPER_FEED', 'iex') # Default to
|
||||
# Convert the string to DataFeed enum
|
||||
try:
|
||||
ACCOUNT1_PAPER_FEED = DataFeed(data_feed_type_str)
|
||||
except ValueError:
|
||||
except nameError:
|
||||
# Handle the case where the environment variable does not match any enum member
|
||||
print(f"Invalid data feed type: {data_feed_type_str} in ACCOUNT1_PAPER_FEED defaulting to 'iex'")
|
||||
ACCOUNT1_PAPER_FEED = DataFeed.SIP
|
||||
@@ -111,7 +128,7 @@ data_feed_type_str = os.environ.get('ACCOUNT1_LIVE_FEED', 'iex') # Default to '
|
||||
# Convert the string to DataFeed enum
|
||||
try:
|
||||
ACCOUNT1_LIVE_FEED = DataFeed(data_feed_type_str)
|
||||
except ValueError:
|
||||
except nameError:
|
||||
# Handle the case where the environment variable does not match any enum member
|
||||
print(f"Invalid data feed type: {data_feed_type_str} in ACCOUNT1_LIVE_FEED defaulting to 'iex'")
|
||||
ACCOUNT1_LIVE_FEED = DataFeed.IEX
|
||||
@@ -129,7 +146,7 @@ data_feed_type_str = os.environ.get('ACCOUNT2_PAPER_FEED', 'iex') # Default to
|
||||
# Convert the string to DataFeed enum
|
||||
try:
|
||||
ACCOUNT2_PAPER_FEED = DataFeed(data_feed_type_str)
|
||||
except ValueError:
|
||||
except nameError:
|
||||
# Handle the case where the environment variable does not match any enum member
|
||||
print(f"Invalid data feed type: {data_feed_type_str} in ACCOUNT2_PAPER_FEED defaulting to 'iex'")
|
||||
ACCOUNT2_PAPER_FEED = DataFeed.IEX
|
||||
@@ -148,7 +165,7 @@ except ValueError:
|
||||
# # Convert the string to DataFeed enum
|
||||
# try:
|
||||
# ACCOUNT2_LIVE_FEED = DataFeed(data_feed_type_str)
|
||||
# except ValueError:
|
||||
# except nameError:
|
||||
# # Handle the case where the environment variable does not match any enum member
|
||||
# print(f"Invalid data feed type: {data_feed_type_str} in ACCOUNT2_LIVE_FEED defaulting to 'iex'")
|
||||
# ACCOUNT2_LIVE_FEED = DataFeed.IEX
|
||||
|
||||
@@ -3,7 +3,7 @@ from uuid import UUID, uuid4
|
||||
from v2realbot.common.model import RunManagerRecord, StrategyInstance, RunDay, StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, RunArchiveChange, Bar, TradeEvent, TestList, Intervals, ConfigItem, InstantIndicator, DataTablesRequest
|
||||
from v2realbot.utils.utils import validate_and_format_time, AttributeDict, zoneNY, zonePRG, safe_get, dict_replace_value, Store, parse_toml_string, json_serial, is_open_hours, send_to_telegram, concatenate_weekdays, transform_data
|
||||
from v2realbot.utils.ilog import delete_logs
|
||||
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
|
||||
from v2realbot.common.model import Trade, TradeDirection, TradeStatus, TradeStoplossType
|
||||
from datetime import datetime
|
||||
from v2realbot.loader.trade_offline_streamer import Trade_Offline_Streamer
|
||||
from threading import Thread, current_thread, Event, enumerate
|
||||
|
||||
@@ -8,9 +8,9 @@ from alpaca.data.timeframe import TimeFrame
|
||||
from v2realbot.strategy.base import StrategyState
|
||||
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, OrderSide
|
||||
from v2realbot.common.model import RunDay, StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, RunArchiveChange, Bar, TradeEvent, TestList, Intervals, ConfigItem, InstantIndicator, DataTablesRequest
|
||||
from v2realbot.utils.utils import AttributeDict, zoneNY, zonePRG, safe_get, dict_replace_value, Store, parse_toml_string, json_serial, is_open_hours, send_to_telegram, concatenate_weekdays, transform_data
|
||||
from v2realbot.utils.utils import AttributeDict, zoneNY, zonePRG, safe_get, dict_replace_value, Store, parse_toml_string, json_serial, is_open_hours, send_to_telegram, concatenate_weekdays, transform_data, gaka
|
||||
from v2realbot.utils.ilog import delete_logs
|
||||
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
|
||||
from v2realbot.common.model import Trade, TradeDirection, TradeStatus, TradeStoplossType
|
||||
from datetime import datetime
|
||||
from v2realbot.loader.trade_offline_streamer import Trade_Offline_Streamer
|
||||
from threading import Thread, current_thread, Event, enumerate
|
||||
@@ -71,8 +71,8 @@ def get_all_runners():
|
||||
if i.run_instance:
|
||||
i.run_profit = round(float(i.run_instance.state.profit),2)
|
||||
i.run_trade_count = len(i.run_instance.state.tradeList)
|
||||
i.run_positions = i.run_instance.state.positions
|
||||
i.run_avgp = round(float(i.run_instance.state.avgp),3)
|
||||
i.run_positions = gaka(i.run_instance.state.account_variables, "positions")
|
||||
i.run_avgp = gaka(i.run_instance.state.account_variables, "avgp", lambda x: round(float(x),3))
|
||||
return (0, db.runners)
|
||||
else:
|
||||
return (0, [])
|
||||
@@ -94,8 +94,8 @@ def get_runner(id: UUID):
|
||||
if str(i.id) == str(id):
|
||||
i.run_profit = round(float(i.run_instance.state.profit),2)
|
||||
i.run_trade_count = len(i.run_instance.state.tradeList)
|
||||
i.run_positions = i.run_instance.state.positions
|
||||
i.run_avgp = round(float(i.run_instance.state.avgp),3)
|
||||
i.run_positions =gaka(i.run_instance.state.account_variables, "positions")
|
||||
i.run_avgp = gaka(i.run_instance.state.account_variables, "avgp", lambda x: round(float(x),3))
|
||||
return (0, i)
|
||||
return (-2, "not found")
|
||||
|
||||
@@ -738,13 +738,14 @@ def populate_metrics_output_directory(strat: StrategyInstance, inter_batch_param
|
||||
|
||||
tradeList = strat.state.tradeList
|
||||
|
||||
trade_dict = AttributeDict(orderid=[],timestamp=[],symbol=[],side=[],order_type=[],qty=[],price=[],position_qty=[])
|
||||
trade_dict = AttributeDict(account=[],orderid=[],timestamp=[],symbol=[],side=[],order_type=[],qty=[],price=[],position_qty=[])
|
||||
if strat.mode == Mode.BT:
|
||||
trade_dict["value"] = []
|
||||
trade_dict["cash"] = []
|
||||
trade_dict["pos_avg_price"] = []
|
||||
for t in tradeList:
|
||||
if t.event == TradeEvent.FILL:
|
||||
trade_dict.account.append(t.account)
|
||||
trade_dict.orderid.append(str(t.order.id))
|
||||
trade_dict.timestamp.append(t.timestamp)
|
||||
trade_dict.symbol.append(t.order.symbol)
|
||||
@@ -768,10 +769,12 @@ def populate_metrics_output_directory(strat: StrategyInstance, inter_batch_param
|
||||
max_positions = max_positions[max_positions['side'] == OrderSide.SELL]
|
||||
max_positions = max_positions.drop(columns=['side'], axis=1)
|
||||
|
||||
res = dict(profit={})
|
||||
res = dict(account_variables={}, profit={})
|
||||
#filt = max_positions['side'] == 'OrderSide.BUY'
|
||||
res["pos_cnt"] = dict(zip(str(max_positions['qty']), max_positions['count']))
|
||||
|
||||
res["account_variables"] = transform_data(strat.state.account_variables, json_serial)
|
||||
|
||||
res["pos_cnt"] = dict(zip(str(max_positions['qty']), max_positions['count']))
|
||||
#naplneni batch sum profitu
|
||||
if inter_batch_params is not None:
|
||||
res["profit"]["batch_sum_profit"] = int(inter_batch_params["batch_profit"])
|
||||
@@ -923,8 +926,8 @@ def archive_runner(runner: Runner, strat: StrategyInstance, inter_batch_params:
|
||||
settings = settings,
|
||||
profit=round(float(strat.state.profit),2),
|
||||
trade_count=len(strat.state.tradeList),
|
||||
end_positions=strat.state.positions,
|
||||
end_positions_avgp=round(float(strat.state.avgp),3),
|
||||
end_positions=gaka(strat.state.account_variables, "positions"),
|
||||
end_positions_avgp=gaka(strat.state.account_variables, "avgp", lambda x: round(float(x),3)),
|
||||
metrics=results_metrics,
|
||||
stratvars_toml=runner.run_stratvars_toml,
|
||||
transferables=strat.state.vars["transferables"]
|
||||
@@ -1264,6 +1267,7 @@ def insert_archive_header(archeader: RunArchive):
|
||||
try:
|
||||
c = conn.cursor()
|
||||
#json_string = orjson.dumps(archeader, default=json_serial, option=orjson.OPT_PASSTHROUGH_DATETIME)
|
||||
#print(archeader)
|
||||
|
||||
res = c.execute("""
|
||||
INSERT INTO runner_header
|
||||
@@ -1271,7 +1275,7 @@ def insert_archive_header(archeader: RunArchive):
|
||||
VALUES
|
||||
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(str(archeader.id), str(archeader.strat_id), archeader.batch_id, archeader.symbol, archeader.name, archeader.note, archeader.started, archeader.stopped, archeader.mode, archeader.account, archeader.bt_from, archeader.bt_to, orjson.dumps(archeader.strat_json).decode('utf-8'), orjson.dumps(archeader.settings).decode('utf-8'), archeader.ilog_save, archeader.profit, archeader.trade_count, archeader.end_positions, archeader.end_positions_avgp, orjson.dumps(archeader.metrics, default=json_serial, option=orjson.OPT_PASSTHROUGH_DATETIME).decode('utf-8'), archeader.stratvars_toml, orjson.dumps(archeader.transferables).decode('utf-8')))
|
||||
(str(archeader.id), str(archeader.strat_id), archeader.batch_id, archeader.symbol, archeader.name, archeader.note, archeader.started, archeader.stopped, archeader.mode, archeader.account, archeader.bt_from, archeader.bt_to, orjson.dumps(archeader.strat_json).decode('utf-8'), orjson.dumps(archeader.settings).decode('utf-8'), archeader.ilog_save, archeader.profit, archeader.trade_count, orjson.dumps(archeader.end_positions).decode('utf-8'), orjson.dumps(archeader.end_positions_avgp).decode('utf-8'), orjson.dumps(archeader.metrics, default=json_serial, option=orjson.OPT_PASSTHROUGH_DATETIME).decode('utf-8'), archeader.stratvars_toml, orjson.dumps(archeader.transferables).decode('utf-8')))
|
||||
|
||||
#retry not yet supported for statement format above
|
||||
#res = execute_with_retry(c,statement)
|
||||
@@ -1646,13 +1650,14 @@ def preview_indicator_byTOML(id: UUID, indicator: InstantIndicator, save: bool =
|
||||
new_inds = AttributeDict(**new_inds)
|
||||
new_tick_inds = {key: [] for key in detail.indicators[1].keys()}
|
||||
new_tick_inds = AttributeDict(**new_tick_inds)
|
||||
interface = BacktestInterface(symbol="X", bt=None)
|
||||
def_account = Account("ACCOUNT1")
|
||||
interface = BacktestInterface(symbol="X", bt=None, account=def_account)
|
||||
|
||||
##dame nastaveni indikatoru do tvaru, ktery stratvars ocekava (pro dynmaicke inicializace)
|
||||
stratvars = AttributeDict(indicators=AttributeDict(**{jmeno:toml_parsed}))
|
||||
#print("stratvars", stratvars)
|
||||
|
||||
state = StrategyState(name="XX", symbol = "X", stratvars = AttributeDict(**stratvars), interface=interface)
|
||||
state = StrategyState(name="XX", symbol = "X", stratvars = AttributeDict(**stratvars), interface=interface, accounts=[def_account], account=def_account)
|
||||
|
||||
#inicializujeme stavove promenne a novy indikator v cilovem dict
|
||||
if output == "bar":
|
||||
|
||||
@@ -5,6 +5,7 @@ from v2realbot.backtesting.backtester import Backtester
|
||||
from datetime import datetime
|
||||
from v2realbot.utils.utils import zoneNY
|
||||
import v2realbot.utils.config_handler as cfh
|
||||
from v2realbot.common.model import Account
|
||||
|
||||
""""
|
||||
backtester methods can be called
|
||||
@@ -16,8 +17,9 @@ both should be backtestable
|
||||
if method are called for the past self.time must be set accordingly
|
||||
"""
|
||||
class BacktestInterface(GeneralInterface):
|
||||
def __init__(self, symbol, bt: Backtester) -> None:
|
||||
def __init__(self, symbol, bt: Backtester, account: Account) -> None:
|
||||
self.symbol = symbol
|
||||
self.account = account
|
||||
self.bt = bt
|
||||
self.count_api_requests = cfh.config_handler.get_val('COUNT_API_REQUESTS')
|
||||
self.mincnt = list([dict(minute=0,count=0)])
|
||||
@@ -43,48 +45,48 @@ class BacktestInterface(GeneralInterface):
|
||||
def buy(self, size = 1, repeat: bool = False):
|
||||
self.count()
|
||||
#add REST API latency
|
||||
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.BUY,size=size,order_type = OrderType.MARKET)
|
||||
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.BUY,size=size,order_type = OrderType.MARKET, account=self.account)
|
||||
|
||||
"""buy limit"""
|
||||
def buy_l(self, price: float, size: int = 1, repeat: bool = False, force: int = 0):
|
||||
self.count()
|
||||
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.BUY,size=size,price=price,order_type = OrderType.LIMIT)
|
||||
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.BUY,size=size,price=price,order_type = OrderType.LIMIT, account=self.account)
|
||||
|
||||
"""sell market"""
|
||||
def sell(self, size = 1, repeat: bool = False):
|
||||
self.count()
|
||||
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.SELL,size=size,order_type = OrderType.MARKET)
|
||||
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.SELL,size=size,order_type = OrderType.MARKET, account=self.account)
|
||||
|
||||
"""sell limit"""
|
||||
async def sell_l(self, price: float, size = 1, repeat: bool = False):
|
||||
self.count()
|
||||
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.SELL,size=size,price=price,order_type = OrderType.LIMIT)
|
||||
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.SELL,size=size,price=price,order_type = OrderType.LIMIT, account=self.account)
|
||||
|
||||
"""replace order"""
|
||||
async def repl(self, orderid: str, price: float = None, size: int = None, repeat: bool = False):
|
||||
self.count()
|
||||
return self.bt.replace_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),id=orderid,size=size,price=price)
|
||||
return self.bt.replace_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),id=orderid,size=size,price=price, account=self.account)
|
||||
|
||||
"""cancel order"""
|
||||
#TBD exec predtim?
|
||||
def cancel(self, orderid: str):
|
||||
self.count()
|
||||
return self.bt.cancel_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'), id=orderid)
|
||||
return self.bt.cancel_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'), id=orderid, account=self.account)
|
||||
|
||||
"""get positions ->(size,avgp)"""
|
||||
#TBD exec predtim?
|
||||
def pos(self):
|
||||
self.count()
|
||||
return self.bt.get_open_position(symbol=self.symbol)
|
||||
return self.bt.get_open_position(symbol=self.symbol, account=self.account)
|
||||
|
||||
"""get open orders ->list(Order)"""
|
||||
def get_open_orders(self, side: OrderSide, symbol: str):
|
||||
self.count()
|
||||
return self.bt.get_open_orders(side=side, symbol=symbol)
|
||||
return self.bt.get_open_orders(side=side, symbol=symbol, account=self.account)
|
||||
|
||||
def get_last_price(self, symbol: str):
|
||||
self.count()
|
||||
return self.bt.get_last_price(time=self.bt.time)
|
||||
return self.bt.get_last_price(time=self.bt.time, account=self.account)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ class LiveInterface(GeneralInterface):
|
||||
return -1
|
||||
|
||||
"""sell limit"""
|
||||
async def sell_l(self, price: float, size = 1, repeat: bool = False):
|
||||
def sell_l(self, price: float, size = 1, repeat: bool = False):
|
||||
self.size = size
|
||||
self.repeat = repeat
|
||||
|
||||
@@ -124,7 +124,7 @@ class LiveInterface(GeneralInterface):
|
||||
return -1
|
||||
|
||||
"""order replace"""
|
||||
async def repl(self, orderid: str, price: float = None, size: int = None, repeatl: bool = False):
|
||||
def repl(self, orderid: str, price: float = None, size: int = None, repeatl: bool = False):
|
||||
|
||||
if not price and not size:
|
||||
print("price or size has to be filled")
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from threading import Thread
|
||||
from threading import Thread, current_thread
|
||||
from alpaca.trading.stream import TradingStream
|
||||
from v2realbot.config import Keys
|
||||
from v2realbot.common.model import Account
|
||||
|
||||
#jelikoz Alpaca podporuje pripojeni libovolneho poctu websocket instanci na order updates
|
||||
#vytvorime pro kazdou bezici instanci vlastni webservisu (jinak bychom museli delat instanci pro kombinaci ACCOUNT1 - LIVE, ACCOUNT1 - PAPER, ACCOUNT2 - PAPER ..)
|
||||
@@ -14,15 +15,16 @@ As Alpaca supports connecting of any number of trade updates clients
|
||||
new instance of this websocket thread is created for each strategy instance.
|
||||
"""""
|
||||
class LiveOrderUpdatesStreamer(Thread):
|
||||
def __init__(self, key: Keys, name: str) -> None:
|
||||
def __init__(self, key: Keys, name: str, account: Account) -> None:
|
||||
self.key = key
|
||||
self.account = account
|
||||
self.strategy = None
|
||||
self.client = TradingStream(api_key=key.API_KEY, secret_key=key.SECRET_KEY, paper=key.PAPER)
|
||||
Thread.__init__(self, name=name)
|
||||
|
||||
#notif dispatcher - pouze 1 strategie
|
||||
async def distributor(self,data):
|
||||
if self.strategy.symbol == data.order.symbol: await self.strategy.order_updates(data)
|
||||
if self.strategy.symbol == data.order.symbol: await self.strategy.order_updates(data, self.account)
|
||||
|
||||
# connects callback to interface object - responses for given symbol are routed to interface callback
|
||||
def connect_callback(self, st):
|
||||
@@ -39,6 +41,6 @@ class LiveOrderUpdatesStreamer(Thread):
|
||||
print("connect strategy first")
|
||||
return
|
||||
self.client.subscribe_trade_updates(self.distributor)
|
||||
print("*"*10, "WS Order Update Streamer started for", self.strategy.name, "*"*10)
|
||||
print("*"*10, "WS Order Update Streamer started for", current_thread().name,"*"*10)
|
||||
self.client.run()
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ from fastapi.security import APIKeyHeader
|
||||
import uvicorn
|
||||
from uuid import UUID
|
||||
from v2realbot.utils.ilog import get_log_window
|
||||
from v2realbot.common.model import RunManagerRecord, StrategyInstance, RunnerView, RunRequest, Trade, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, Bar, RunArchiveChange, TestList, ConfigItem, InstantIndicator, DataTablesRequest, AnalyzerInputs
|
||||
from v2realbot.common.model import RunManagerRecord, StrategyInstance, RunnerView, RunRequest, TradeView, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, Bar, RunArchiveChange, TestList, ConfigItem, InstantIndicator, DataTablesRequest, AnalyzerInputs
|
||||
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Depends, HTTPException, status, WebSocketException, Cookie, Query, Request
|
||||
from fastapi.responses import FileResponse, StreamingResponse, JSONResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
@@ -18,6 +18,7 @@ from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||
from v2realbot.enums.enums import Env, Mode
|
||||
from typing import Annotated
|
||||
import os
|
||||
import psutil
|
||||
import uvicorn
|
||||
import orjson
|
||||
from queue import Queue, Empty
|
||||
@@ -333,7 +334,7 @@ def stop_all_runners():
|
||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Error: {res}:{id}")
|
||||
|
||||
@app.get("/tradehistory/{symbol}", dependencies=[Depends(api_key_auth)])
|
||||
def get_trade_history(symbol: str, timestamp_from: float, timestamp_to:float) -> list[Trade]:
|
||||
def get_trade_history(symbol: str, timestamp_from: float, timestamp_to:float) -> list[TradeView]:
|
||||
res, set = cs.get_trade_history(symbol, timestamp_from, timestamp_to)
|
||||
if res == 0:
|
||||
return set
|
||||
@@ -1025,7 +1026,25 @@ def get_metadata(model_name: str):
|
||||
# "last_modified": os.path.getmtime(model_path),
|
||||
# # ... other metadata fields ...
|
||||
# }
|
||||
|
||||
@app.get("/system-info")
|
||||
def get_system_info():
|
||||
"""Get system info, e.g. disk free space, used percentage ... """
|
||||
disk_total = round(psutil.disk_usage('/').total / 1024**3, 1)
|
||||
disk_used = round(psutil.disk_usage('/').used / 1024**3, 1)
|
||||
disk_free = round(psutil.disk_usage('/').free / 1024**3, 1)
|
||||
disk_used_percentage = round(psutil.disk_usage('/').percent, 1)
|
||||
# memory_total = round(psutil.virtual_memory().total / 1024**3, 1)
|
||||
# memory_perc = round(psutil.virtual_memory().percent, 1)
|
||||
# cpu_time_user = round(psutil.cpu_times().user,1)
|
||||
# cpu_time_system = round(psutil.cpu_times().system,1)
|
||||
# cpu_time_idle = round(psutil.cpu_times().idle,1)
|
||||
# network_sent = round(psutil.net_io_counters().bytes_sent / 1024**3, 6)
|
||||
# network_recv = round(psutil.net_io_counters().bytes_recv / 1024**3, 6)
|
||||
return {"disk_space": {"total": disk_total, "used": disk_used, "free" : disk_free, "used_percentage" : disk_used_percentage},
|
||||
# "memory": {"total": memory_total, "used_percentage": memory_perc},
|
||||
# "cpu_time" : {"user": cpu_time_user, "system": cpu_time_system, "idle": cpu_time_idle},
|
||||
# "network": {"sent": network_sent, "received": network_recv}
|
||||
}
|
||||
|
||||
# Thread function to insert data from the queue into the database
|
||||
def insert_queue2db():
|
||||
|
||||
@@ -12,7 +12,7 @@ import numpy as np
|
||||
import v2realbot.controller.services as cs
|
||||
from rich import print
|
||||
from v2realbot.common.model import AnalyzerInputs
|
||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||
from pathlib import Path
|
||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||
|
||||
@@ -12,7 +12,7 @@ import numpy as np
|
||||
import v2realbot.controller.services as cs
|
||||
from rich import print
|
||||
from v2realbot.common.model import AnalyzerInputs
|
||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||
from pathlib import Path
|
||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||
|
||||
@@ -11,7 +11,7 @@ import numpy as np
|
||||
import v2realbot.controller.services as cs
|
||||
from rich import print
|
||||
from v2realbot.common.model import AnalyzerInputs
|
||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||
from pathlib import Path
|
||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||
|
||||
@@ -11,7 +11,7 @@ import numpy as np
|
||||
import v2realbot.controller.services as cs
|
||||
from rich import print
|
||||
from v2realbot.common.model import AnalyzerInputs
|
||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||
from pathlib import Path
|
||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||
|
||||
@@ -11,7 +11,7 @@ import numpy as np
|
||||
import v2realbot.controller.services as cs
|
||||
from rich import print
|
||||
from v2realbot.common.model import AnalyzerInputs
|
||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||
from pathlib import Path
|
||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||
|
||||
@@ -11,7 +11,7 @@ import numpy as np
|
||||
import v2realbot.controller.services as cs
|
||||
from rich import print
|
||||
from v2realbot.common.model import AnalyzerInputs
|
||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||
from pathlib import Path
|
||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||
|
||||
@@ -11,7 +11,7 @@ import numpy as np
|
||||
import v2realbot.controller.services as cs
|
||||
from rich import print
|
||||
from v2realbot.common.model import AnalyzerInputs
|
||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||
from pathlib import Path
|
||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||
|
||||
@@ -11,7 +11,7 @@ import numpy as np
|
||||
import v2realbot.controller.services as cs
|
||||
from rich import print
|
||||
from v2realbot.common.model import AnalyzerInputs
|
||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||
from pathlib import Path
|
||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||
|
||||
@@ -12,7 +12,7 @@ import numpy as np
|
||||
import v2realbot.controller.services as cs
|
||||
from rich import print
|
||||
from v2realbot.common.model import AnalyzerInputs
|
||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||
from pathlib import Path
|
||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||
|
||||
@@ -10,7 +10,7 @@ from enum import Enum
|
||||
import numpy as np
|
||||
import v2realbot.controller.services as cs
|
||||
from rich import print
|
||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||
from pathlib import Path
|
||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||
|
||||
@@ -10,7 +10,7 @@ from enum import Enum
|
||||
import numpy as np
|
||||
import v2realbot.controller.services as cs
|
||||
from rich import print
|
||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||
from pathlib import Path
|
||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||
|
||||
@@ -10,7 +10,7 @@ from enum import Enum
|
||||
import numpy as np
|
||||
import v2realbot.controller.services as cs
|
||||
from rich import print
|
||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||
from pathlib import Path
|
||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||
|
||||
@@ -11,7 +11,7 @@ import numpy as np
|
||||
import v2realbot.controller.services as cs
|
||||
from rich import print
|
||||
from v2realbot.common.model import AnalyzerInputs
|
||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||
from pathlib import Path
|
||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||
@@ -23,7 +23,7 @@ from collections import defaultdict
|
||||
from scipy.stats import zscore
|
||||
from io import BytesIO
|
||||
from typing import Tuple, Optional, List
|
||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
|
||||
def load_trades(runner_ids: List = None, batch_id: str = None) -> Tuple[int, List[Trade], int]:
|
||||
if runner_ids is None and batch_id is None:
|
||||
|
||||
@@ -10,7 +10,7 @@ from enum import Enum
|
||||
import numpy as np
|
||||
import v2realbot.controller.services as cs
|
||||
from rich import print
|
||||
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get, print
|
||||
from pathlib import Path
|
||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||
|
||||
@@ -4,7 +4,7 @@ from uuid import UUID, uuid4
|
||||
from v2realbot.enums.enums import Moddus, SchedulerStatus, RecordType, StartBarAlign, Mode, Account, OrderSide
|
||||
from v2realbot.common.model import RunManagerRecord, StrategyInstance, RunDay, StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, RunArchiveChange, Bar, TradeEvent, TestList, Intervals, ConfigItem, InstantIndicator, DataTablesRequest, Market
|
||||
from v2realbot.utils.utils import validate_and_format_time, AttributeDict, zoneNY, zonePRG, safe_get, dict_replace_value, Store, parse_toml_string, json_serial, is_open_hours, send_to_telegram, concatenate_weekdays, transform_data
|
||||
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
|
||||
from v2realbot.common.model import Trade, TradeDirection, TradeStatus, TradeStoplossType
|
||||
from datetime import datetime
|
||||
from v2realbot.config import JOB_LOG_FILE, STRATVARS_UNCHANGEABLES, ACCOUNT1_PAPER_API_KEY, ACCOUNT1_PAPER_SECRET_KEY, ACCOUNT1_LIVE_API_KEY, ACCOUNT1_LIVE_SECRET_KEY, DATA_DIR, MEDIA_DIRECTORY, RUNNER_DETAIL_DIRECTORY
|
||||
import numpy as np
|
||||
|
||||
@@ -131,9 +131,29 @@
|
||||
|
||||
<!-- <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.41.0/min/vs/editor/editor.main.js"></script> -->
|
||||
<!-- <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.41.0/min/vs/loader.min.js"></script> -->
|
||||
<script src="/static/js/systeminfo.js"> </script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="main" class="mainConteiner flex-container content">
|
||||
<div id="system-info" class="flex-items">
|
||||
<label data-bs-toggle="collapse" data-bs-target="#system-info-inner" aria-expanded="true">
|
||||
<h4>System Info </h4>
|
||||
</label>
|
||||
<div id="system-info-inner" class="collapse">
|
||||
<div id="system-info-output"></div>
|
||||
<div id="graphical-output">
|
||||
<div id="disk-gauge-container">
|
||||
<span id="title"> Disk Space: </span>
|
||||
<span id="free-space">Free: -- GB</span> |
|
||||
<span id="total-space">Total: -- GB</span> |
|
||||
<span id="used-percent">Used: -- %</span>
|
||||
<div id="disk-gauge">
|
||||
<div id="disk-gauge-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="chartContainer" class="flex-items">
|
||||
<label data-bs-toggle="collapse" data-bs-target="#chartContainerInner" aria-expanded="true">
|
||||
<h4>Chart</h4>
|
||||
@@ -215,7 +235,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div id="hist-trades" class="flex-items">
|
||||
<div id="form-trades">
|
||||
<div id="form-trades">
|
||||
<label data-bs-toggle="collapse" data-bs-target="#trades-data">
|
||||
<h4>Trade History</h4>
|
||||
</label>
|
||||
@@ -230,6 +250,7 @@
|
||||
<!-- <table id="trades-data-table" class="dataTable no-footer" style="width: 300px;display: contents;"></table> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="runner-table" class="flex-items">
|
||||
<label data-bs-toggle="collapse" data-bs-target="#runner-table-inner">
|
||||
<h4>Running Strategies</h4>
|
||||
|
||||
@@ -11,6 +11,44 @@ var markersLine = null
|
||||
var avgBuyLine = null
|
||||
var profitLine = null
|
||||
var slLine = []
|
||||
|
||||
//create function which for each ACCOUNT1, ACCOUNT2 or ACCOUNT3 returns color for buy and color for sell - which can be strings representing color
|
||||
//HELPERS FUNCTION - will go to utils
|
||||
/**
|
||||
* Returns an object containing the colors for buy and sell for the specified account.
|
||||
*
|
||||
* Parameters:
|
||||
* account (string): The account for which to retrieve the colors (ACCOUNT1, ACCOUNT2, or ACCOUNT3).
|
||||
*
|
||||
* Returns:
|
||||
* object: An object with 'buy' and 'sell' properties containing the corresponding color strings.
|
||||
*
|
||||
* Account 1:
|
||||
#FF6B6B, #FF9999
|
||||
Account 2:
|
||||
#4ECDC4, #83E8E1
|
||||
Account 3:
|
||||
#FFD93D, #FFE787
|
||||
Account 4:
|
||||
#6C5CE7, #A29BFE
|
||||
Another option for colors:
|
||||
|
||||
#1F77B4 (Entry) and #AEC7E8 (Exit)
|
||||
#FF7F0E (Entry) and #FFBB78 (Exit)
|
||||
#2CA02C (Entry) and #98DF8A (Exit)
|
||||
#D62728 (Entry) and #FF9896 (Exit)
|
||||
*/
|
||||
function getAccountColors(account) {
|
||||
const accountColors = {
|
||||
ACCOUNT1: { accid: 'A1', buy: '#FF7F0E', sell: '#FFBB78' },
|
||||
ACCOUNT2: { accid: 'A2',buy: '#1F77B4', sell: '#AEC7E8' },
|
||||
ACCOUNT3: { accid: 'A3',buy: '#2CA02C', sell: '#98DF8A' },
|
||||
ACCOUNT4: { accid: 'A4',buy: '#D62728', sell: '#FF9896' },
|
||||
ACCOUNT5: { accid: 'A5',buy: 'purple', sell: 'orange' }
|
||||
};
|
||||
return accountColors[account] || { buy: '#37cade', sell: 'red' };
|
||||
}
|
||||
|
||||
//TRANSFORM object returned from REST API get_arch_run_detail
|
||||
//to series and markers required by lightweigth chart
|
||||
//input array object bars = { high: [1,2,3], time: [1,2,3], close: [2,2,2]...}
|
||||
@@ -34,6 +72,11 @@ function transform_data(data) {
|
||||
//cas of first record, nekdy jsou stejny - musim pridat setinku
|
||||
prev_cas = 0
|
||||
if ((data.ext_data !== null) && (data.ext_data.sl_history)) {
|
||||
///sort sl_history according to order id string - i need all same order id together
|
||||
data.ext_data.sl_history.sort(function (a, b) {
|
||||
return a.id.localeCompare(b.id);
|
||||
});
|
||||
|
||||
data.ext_data.sl_history.forEach((histRecord, index, array) => {
|
||||
|
||||
//console.log("plnime")
|
||||
@@ -48,6 +91,7 @@ function transform_data(data) {
|
||||
//init nova sada
|
||||
sl_line_sada = []
|
||||
sl_line_markers_sada = []
|
||||
sline_color = "#f5aa42"
|
||||
}
|
||||
|
||||
prev_id = histRecord.id
|
||||
@@ -65,12 +109,21 @@ function transform_data(data) {
|
||||
sline = {}
|
||||
sline["time"] = cas
|
||||
sline["value"] = histRecord.sl_val
|
||||
if (histRecord.account) {
|
||||
const accColors = getAccountColors(histRecord.account)
|
||||
sline_color = histRecord.direction == "long" ? accColors.buy : accColors.sell //idealne
|
||||
sline["color"] = sline_color
|
||||
}
|
||||
|
||||
sl_line_sada.push(sline)
|
||||
|
||||
//ZDE JSEM SKONCIL
|
||||
//COLOR SE NASTAVUJE V SERIES OPTIONS POZDEJI - nejak vymyslet
|
||||
|
||||
sline_markers = {}
|
||||
sline_markers["time"] = cas
|
||||
sline_markers["position"] = "inBar"
|
||||
sline_markers["color"] = "#f5aa42"
|
||||
sline_markers["color"] = sline_color
|
||||
//sline_markers["shape"] = "circle"
|
||||
//console.log("SHOW_SL_DIGITS",SHOW_SL_DIGITS)
|
||||
sline_markers["text"] = SHOW_SL_DIGITS ? histRecord.sl_val.toFixed(3) : ""
|
||||
@@ -239,31 +292,33 @@ function transform_data(data) {
|
||||
// //a_markers["text"] = CHART_SHOW_TEXT ? trade.position_qty + "/" + parseFloat(trade.pos_avg_price).toFixed(3) :trade.position_qty
|
||||
// avgp_markers.push(a_markers)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const { accid: accountId,buy: buyColor, sell: sellColor } = getAccountColors(trade.account);
|
||||
|
||||
//buy sell markery
|
||||
marker = {}
|
||||
marker["time"] = timestamp;
|
||||
// marker["position"] = (trade.order.side == "buy") ? "belowBar" : "aboveBar"
|
||||
marker["position"] = (trade.order.side == "buy") ? "aboveBar" : "aboveBar"
|
||||
marker["color"] = (trade.order.side == "buy") ? "#37cade" : "red"
|
||||
marker["color"] = (trade.order.side == "buy") ? buyColor : sellColor
|
||||
//marker["shape"] = (trade.order.side == "buy") ? "arrowUp" : "arrowDown"
|
||||
marker["shape"] = (trade.order.side == "buy") ? "arrowUp" : "arrowDown"
|
||||
//marker["text"] = trade.qty + "/" + trade.price
|
||||
qt_optimized = (trade.order.qty % 1000 === 0) ? (trade.order.qty / 1000).toFixed(1) + 'K' : trade.order.qty
|
||||
|
||||
marker["text"] = accountId + " " //account shortcut
|
||||
if (CHART_SHOW_TEXT) {
|
||||
//včetně qty
|
||||
//marker["text"] = qt_optimized + "@" + trade.price
|
||||
|
||||
//bez qty
|
||||
marker["text"] = trade.price
|
||||
marker["text"] += trade.price
|
||||
closed_trade_marker_and_profit = (trade.profit) ? "c" + trade.profit.toFixed(1) + "/" + trade.profit_sum.toFixed(1) : "c"
|
||||
marker["text"] += (trade.position_qty == 0) ? closed_trade_marker_and_profit : ""
|
||||
} else {
|
||||
closed_trade_marker_and_profit = (trade.profit) ? "c" + trade.profit.toFixed(1) + "/" + trade.profit_sum.toFixed(1) : "c"
|
||||
marker["text"] = (trade.position_qty == 0) ? closed_trade_marker_and_profit : trade.price.toFixed(3)
|
||||
marker["text"] += (trade.position_qty == 0) ? closed_trade_marker_and_profit : trade.price.toFixed(3)
|
||||
}
|
||||
|
||||
markers.push(marker)
|
||||
@@ -844,7 +899,7 @@ function display_buy_markers(data) {
|
||||
//console.log("uvnitr")
|
||||
slLine_temp = chart.addLineSeries({
|
||||
// title: "avgpbuyline",
|
||||
color: '#e4c76d',
|
||||
color: slRecord[0]["color"] ? slRecord[0]["color"] : '#e4c76d',
|
||||
// color: 'transparent',
|
||||
lineWidth: 1,
|
||||
lastValueVisible: false
|
||||
|
||||
31
v2realbot/static/js/systeminfo.js
Normal file
31
v2realbot/static/js/systeminfo.js
Normal file
@@ -0,0 +1,31 @@
|
||||
function get_system_info() {
|
||||
console.log('Button get system status clicked')
|
||||
$.ajax({
|
||||
url: '/system-info',
|
||||
type: 'GET',
|
||||
beforeSend: function (xhr) {
|
||||
xhr.setRequestHeader('X-API-Key',
|
||||
API_KEY); },
|
||||
success: function(response) {
|
||||
$.each(response, function(index, item) {
|
||||
if (index=="disk_space") {
|
||||
$('#disk-gauge-bar').css('width', response.disk_space.used_percentage + '%');
|
||||
$('#free-space').text('Free: ' + response.disk_space.free + ' GB');
|
||||
$('#total-space').text('Total: ' + response.disk_space.total + ' GB');
|
||||
$('#used-percent').text('Used: ' + response.disk_space.used_percentage + '%');
|
||||
} else {
|
||||
var formatted_item = JSON.stringify(item, null, 4)
|
||||
$('#system-info-output').append('<p>' + index + ': ' + formatted_item + '</p>');
|
||||
}
|
||||
});
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
$('#disk-gauge-bar').html('An error occurred: ' + error + xhr.responseText + status);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$(document).ready(function(){
|
||||
get_system_info()
|
||||
});
|
||||
@@ -172,7 +172,7 @@ function initialize_archiveRecords() {
|
||||
{
|
||||
targets: [13,14,15],
|
||||
render: function ( data, type, row ) {
|
||||
return '<div class="tdsmall">'+data+'</div>'
|
||||
return '<div class="tdsmall">'+JSON.stringify(data, null, 2)+'</div>'
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user