Compare commits
5 Commits
feature/mu
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| f04c3e9f12 | |||
| faaaa65081 | |||
| 773f5a3ef8 | |||
| 9d1ef83733 | |||
| ceb69d696b |
29
README.md
29
README.md
@ -1,29 +1,24 @@
|
|||||||
# V2TRADING - Advanced Algorithmic Trading Platform
|
# V2TRADING - Algorithmic Trading Platform with Frontend
|
||||||
|
|
||||||
## Overview
|
## 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.
|
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.
|
- **Trading Engine**: Processes tick data in real time, aggregating data and managing trade 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.
|
- **Backtesting**: tick-by tick backtesting, down to millisecond accuracy, mirrors live trading environments and is vital for developing and testing high(er)-frequency trading strategies.
|
||||||
|
|
||||||
- **Custom Data Aggregation:** The platform includes a data aggregator that allows for custom aggregation rules. This flexibility supports a variety of data analysis approaches, including non-time based bars and other unique criteria.
|
- **Configuration**: robust configuration via TOML
|
||||||
|
|
||||||
|
- **Frontend**: Frontend to support research to backtesting to paper trading workflow, including lightweight charts.
|
||||||
|
|
||||||
|
- **Custom Data Aggregation:** Custom time based, volume based, dollar based and renko bars aggregators based on tick-by-tick data.
|
||||||
|
|
||||||
- **Indicators** Contains inbuild [tulipy](https://tulipindicators.org/list) [ta-lib](https://ta-lib.github.io/ta-lib-python/) and templates for custom build multioutputs stateful indicators.
|
- **Indicators** Contains inbuild [tulipy](https://tulipindicators.org/list) [ta-lib](https://ta-lib.github.io/ta-lib-python/) and templates for custom build multioutputs stateful indicators.
|
||||||
|
|
||||||
- **Machine Learning Integration:** Recently, the platform has expanded to incorporate machine learning capabilities. This includes modules for both training and inference, supporting the complete ML lifecycle. These ML models can be utilized within trading strategies for classification and exploiting statistical advantages.
|
- **Machine Learning Integration:** Includes modules for both training and inference, supporting the complete ML lifecycle.
|
||||||
|
|
||||||
**Technology Stack**
|
**Gui examples**
|
||||||
**Backend and API:** The backbone of the platform is built with Python, utilizing libraries such as FastAPI, NumPy, Keras, and JAX, ensuring high performance and scalability.
|
|
||||||
**Frontend:** The client-side is developed with Vanilla JavaScript and jQuery, employing LightweightCharts for charting purposes. Additional modules enhance the platform's functionality. The frontend is slated for a future refactoring to modern frameworks like Vue.js and Vuetify for a more robust user interface.
|
|
||||||
|
|
||||||
While the platform is fully functional and growing, ongoing development is planned, particularly in the realm of frontend enhancements and further integration of advanced machine learning techniques.
|
|
||||||
|
|
||||||
**Contributions**
|
|
||||||
Contributions to this project are welcome. Whether it's improving the frontend, enhancing the backend capabilities, or experimenting with new trading strategies and machine learning models, your input can help take this platform to the next level.
|
|
||||||
|
|
||||||
This repository represents a sophisticated and evolving tool for algorithmic traders, offering precision, speed, and a level of customization that is unparalleled in open-source systems. Join us in shaping the future of algorithmic trading.
|
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
Main screen with entry/exit points and stoploss lines<br>
|
Main screen with entry/exit points and stoploss lines<br>
|
||||||
@ -50,6 +45,10 @@ This repository represents a sophisticated and evolving tool for algorithmic tra
|
|||||||
<img width="700" alt="Strategy analytical tools" src="https://github.com/drew2323/v2trading/assets/28433232/4bf8b3c3-e430-4250-831a-e5876bb6b743">
|
<img width="700" alt="Strategy analytical tools" src="https://github.com/drew2323/v2trading/assets/28433232/4bf8b3c3-e430-4250-831a-e5876bb6b743">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
**Backend and API:** The backbone of the platform is built with Python, utilizing libraries such as FastAPI, NumPy, Keras, and JAX, ensuring high performance and scalability.
|
||||||
|
**Frontend:** The client-side is developed with Vanilla JavaScript and jQuery, employing LightweightCharts for charting purposes. Additional modules enhance the platform's functionality. The frontend is slated for a future refactoring to modern frameworks like Vue.js and Vuetify for a more robust user interface.
|
||||||
|
|
||||||
|
**Documentation** Public docs in in progress. Some can be found on [knowledge base](trading.mujdenik.eu) but first please request access. Some analysis documents can be found on [shared google doc folder](https://drive.google.com/drive/folders/1WmYG8oDGXO-lVTLVs9knAmMTmQL4dZt6?usp=drive_link).
|
||||||
|
|
||||||
# Installation Instructions
|
# 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.
|
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.
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@ -1691,7 +1691,7 @@
|
|||||||
"name": "python",
|
"name": "python",
|
||||||
"nbconvert_exporter": "python",
|
"nbconvert_exporter": "python",
|
||||||
"pygments_lexer": "ipython3",
|
"pygments_lexer": "ipython3",
|
||||||
"version": "3.10.11"
|
"version": "3.10.10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nbformat": 4,
|
"nbformat": 4,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,932 +0,0 @@
|
|||||||
{
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
@ -1,782 +0,0 @@
|
|||||||
{
|
|
||||||
"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,9 +1,9 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
|
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
from v2realbot.strategyblocks.activetrade.helpers import get_max_profit_price, get_profit_target_price, get_signal_section_directive, keyword_conditions_met
|
from v2realbot.strategyblocks.activetrade.helpers import get_max_profit_price, get_profit_target_price, get_override_for_active_trade, keyword_conditions_met
|
||||||
from v2realbot.utils.utils import safe_get
|
from v2realbot.utils.utils import safe_get
|
||||||
# FIBONACCI PRO PROFIT A SL
|
# FIBONACCI PRO PROFIT A SL
|
||||||
|
|
||||||
@ -63,10 +63,10 @@ class SLOptimizer:
|
|||||||
|
|
||||||
def initialize_levels(self, state):
|
def initialize_levels(self, state):
|
||||||
directive_name = 'SL_opt_exit_levels_'+str(self.direction)
|
directive_name = 'SL_opt_exit_levels_'+str(self.direction)
|
||||||
SL_opt_exit_levels = get_signal_section_directive(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
SL_opt_exit_levels = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
||||||
|
|
||||||
directive_name = 'SL_opt_exit_sizes_'+str(self.direction)
|
directive_name = 'SL_opt_exit_sizes_'+str(self.direction)
|
||||||
SL_opt_exit_sizes = get_signal_section_directive(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
SL_opt_exit_sizes = get_override_for_active_trade(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:
|
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")
|
print("no directives found: SL_opt_exit_levels/SL_opt_exit_sizes")
|
||||||
|
|||||||
@ -1,2 +0,0 @@
|
|||||||
import locale
|
|
||||||
print(locale.getdefaultlocale())
|
|
||||||
@ -59,12 +59,25 @@ Hlavní loop:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
def next(data, state: StrategyState):
|
def next(data, state: StrategyState):
|
||||||
print(10*"*", state.account_variables)
|
##print(10*"*","NEXT START",10*"*")
|
||||||
|
# important vars state.avgp, state.positions, state.vars, data
|
||||||
|
|
||||||
|
#indicators moved to call_next in upper class
|
||||||
|
|
||||||
execute_prescribed_trades(state, data)
|
#pokud mame prazdne pozice a neceka se na nic
|
||||||
signal_search(state, data)
|
if state.positions == 0 and state.vars.pending is None:
|
||||||
execute_prescribed_trades(state, data) #pro jistotu ihned zpracujeme
|
#vykoname trady ve fronte
|
||||||
manage_active_trade(state, data)
|
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)
|
||||||
|
|
||||||
def init(state: StrategyState):
|
def init(state: StrategyState):
|
||||||
#place to declare new vars
|
#place to declare new vars
|
||||||
@ -75,13 +88,13 @@ def init(state: StrategyState):
|
|||||||
|
|
||||||
#nove atributy na rizeni tradu
|
#nove atributy na rizeni tradu
|
||||||
#identifikuje provedenou změnu na Tradu (neděláme změny dokud nepřijde potvrzeni z notifikace)
|
#identifikuje provedenou změnu na Tradu (neděláme změny dokud nepřijde potvrzeni z notifikace)
|
||||||
#state.vars.pending = None #nahrazeno pebnding pod accountem state.account_variables[account.name].pending
|
state.vars.pending = None
|
||||||
#obsahuje aktivni Trade a jeho nastaveni
|
#obsahuje aktivni Trade a jeho nastaveni
|
||||||
#state.vars.activeTrade = None #pending/Trade moved to account_variables
|
state.vars.activeTrade = None #pending/Trade
|
||||||
#obsahuje pripravene Trady ve frontě
|
#obsahuje pripravene Trady ve frontě
|
||||||
state.vars.prescribedTrades = []
|
state.vars.prescribedTrades = []
|
||||||
#flag pro reversal
|
#flag pro reversal
|
||||||
#state.vars.requested_followup = None #nahrazeno pod accountem
|
state.vars.requested_followup = None
|
||||||
|
|
||||||
#TODO presunout inicializaci work_dict u podminek - sice hodnoty nepujdou zmenit, ale zlepsi se performance
|
#TODO presunout inicializaci work_dict u podminek - sice hodnoty nepujdou zmenit, ale zlepsi se performance
|
||||||
#pripadne udelat refresh kazdych x-iterací
|
#pripadne udelat refresh kazdych x-iterací
|
||||||
@ -89,8 +102,9 @@ def init(state: StrategyState):
|
|||||||
state.vars.mode = None
|
state.vars.mode = None
|
||||||
state.vars.last_50_deltas = []
|
state.vars.last_50_deltas = []
|
||||||
state.vars.next_new = 0
|
state.vars.next_new = 0
|
||||||
state.vars.last_entry_index = None #mponechano obecne pro vsechny accounty
|
state.vars.last_buy_index = None
|
||||||
state.vars.last_exit_index = None #obecna varianta ponechana
|
state.vars.last_exit_index = None
|
||||||
|
state.vars.last_in_index = None
|
||||||
state.vars.last_update_time = 0
|
state.vars.last_update_time = 0
|
||||||
state.vars.reverse_position_waiting_amount = 0
|
state.vars.reverse_position_waiting_amount = 0
|
||||||
#INIT promenne, ktere byly zbytecne ve stratvars
|
#INIT promenne, ktere byly zbytecne ve stratvars
|
||||||
|
|||||||
@ -39,7 +39,7 @@
|
|||||||
"""
|
"""
|
||||||
from uuid import UUID, uuid4
|
from uuid import UUID, uuid4
|
||||||
from alpaca.trading.enums import OrderSide, OrderStatus, TradeEvent, OrderType
|
from alpaca.trading.enums import OrderSide, OrderStatus, TradeEvent, OrderType
|
||||||
from v2realbot.common.model import TradeUpdate, Order, Account
|
from v2realbot.common.model import TradeUpdate, Order
|
||||||
from rich import print as printanyway
|
from rich import print as printanyway
|
||||||
import threading
|
import threading
|
||||||
import asyncio
|
import asyncio
|
||||||
@ -61,7 +61,6 @@ import dash_bootstrap_components as dbc
|
|||||||
from dash.dependencies import Input, Output
|
from dash.dependencies import Input, Output
|
||||||
from dash import dcc, html, dash_table, Dash
|
from dash import dcc, html, dash_table, Dash
|
||||||
import v2realbot.utils.config_handler as cfh
|
import v2realbot.utils.config_handler as cfh
|
||||||
from typing import Set
|
|
||||||
""""
|
""""
|
||||||
LATENCY DELAYS
|
LATENCY DELAYS
|
||||||
.000 trigger - last_trade_time (.4246266)
|
.000 trigger - last_trade_time (.4246266)
|
||||||
@ -75,20 +74,7 @@ lock = threading.Lock
|
|||||||
#todo nejspis dat do classes, aby se mohlo backtestovat paralelne
|
#todo nejspis dat do classes, aby se mohlo backtestovat paralelne
|
||||||
#ted je globalni promena last_time_now a self.account a cash
|
#ted je globalni promena last_time_now a self.account a cash
|
||||||
class Backtester:
|
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)
|
#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
|
#it is set by every iteration of BT or before fill callback - allowing past events to happen
|
||||||
self.time = None
|
self.time = None
|
||||||
@ -97,7 +83,6 @@ class Backtester:
|
|||||||
self.btdata = btdata
|
self.btdata = btdata
|
||||||
self.backtest_start = None
|
self.backtest_start = None
|
||||||
self.backtest_end = None
|
self.backtest_end = None
|
||||||
self.accounts = accounts
|
|
||||||
self.cash_init = cash
|
self.cash_init = cash
|
||||||
#backtesting period
|
#backtesting period
|
||||||
self.bp_from = bp_from
|
self.bp_from = bp_from
|
||||||
@ -105,10 +90,9 @@ class Backtester:
|
|||||||
self.cash = cash
|
self.cash = cash
|
||||||
self.cash_reserved_for_shorting = 0
|
self.cash_reserved_for_shorting = 0
|
||||||
self.trades = []
|
self.trades = []
|
||||||
self.internal_account = { account.name:{self.symbol: [0, 0]} for account in accounts }
|
self.account = { "BAC": [0, 0] }
|
||||||
# { "ACCOUNT1": {}"BAC": [avgp, size]}, .... }
|
# { "BAC": [avgp, size] }
|
||||||
self.open_orders =[] #open orders shared for all accounts, account being an attribute
|
self.open_orders =[]
|
||||||
|
|
||||||
# self.open_orders = [Order(id=uuid4(),
|
# self.open_orders = [Order(id=uuid4(),
|
||||||
# submitted_at = datetime(2023, 3, 17, 9, 30, 0, 0, tzinfo=zoneNY),
|
# submitted_at = datetime(2023, 3, 17, 9, 30, 0, 0, tzinfo=zoneNY),
|
||||||
# symbol = "BAC",
|
# symbol = "BAC",
|
||||||
@ -126,8 +110,6 @@ class Backtester:
|
|||||||
# side = OrderSide.BUY)]
|
# side = OrderSide.BUY)]
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
||||||
|
|
||||||
def execute_orders_and_callbacks(self, intime: float):
|
def execute_orders_and_callbacks(self, intime: float):
|
||||||
"""""
|
"""""
|
||||||
Voláno ze strategie před každou iterací s časem T.
|
Voláno ze strategie před každou iterací s časem T.
|
||||||
@ -184,7 +166,7 @@ class Backtester:
|
|||||||
|
|
||||||
for order in self.open_orders:
|
for order in self.open_orders:
|
||||||
#pokud je vyplneny symbol, tak jedeme jen tyto, jinak vsechny
|
#pokud je vyplneny symbol, tak jedeme jen tyto, jinak vsechny
|
||||||
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)
|
print(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:
|
if order.canceled_at:
|
||||||
#ic("deleting canceled order",order.id)
|
#ic("deleting canceled order",order.id)
|
||||||
todel.append(order)
|
todel.append(order)
|
||||||
@ -366,22 +348,21 @@ class Backtester:
|
|||||||
|
|
||||||
#ic(o.filled_at, o.filled_avg_price)
|
#ic(o.filled_at, o.filled_avg_price)
|
||||||
|
|
||||||
a = self.update_internal_account(o = o)
|
a = self.update_account(o = o)
|
||||||
if a < 0:
|
if a < 0:
|
||||||
tlog("update_account ERROR")
|
tlog("update_account ERROR")
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
trade = TradeUpdate(account=o.account,
|
trade = TradeUpdate(order = o,
|
||||||
order = o,
|
|
||||||
event = TradeEvent.FILL,
|
event = TradeEvent.FILL,
|
||||||
execution_id = str(uuid4()),
|
execution_id = str(uuid4()),
|
||||||
timestamp = datetime.fromtimestamp(fill_time),
|
timestamp = datetime.fromtimestamp(fill_time),
|
||||||
position_qty= self.internal_account[o.account.name][o.symbol][0],
|
position_qty= self.account[o.symbol][0],
|
||||||
price=float(fill_price),
|
price=float(fill_price),
|
||||||
qty = o.qty,
|
qty = o.qty,
|
||||||
value = float(o.qty*fill_price),
|
value = float(o.qty*fill_price),
|
||||||
cash = self.cash,
|
cash = self.cash,
|
||||||
pos_avg_price = self.internal_account[o.account.name][o.symbol][1])
|
pos_avg_price = self.account[o.symbol][1])
|
||||||
|
|
||||||
self.trades.append(trade)
|
self.trades.append(trade)
|
||||||
|
|
||||||
@ -398,49 +379,49 @@ class Backtester:
|
|||||||
self.time = time + float(cfh.config_handler.get_val('BT_DELAYS','fill_to_not'))
|
self.time = time + float(cfh.config_handler.get_val('BT_DELAYS','fill_to_not'))
|
||||||
print("current bt.time",self.time)
|
print("current bt.time",self.time)
|
||||||
#print("FILL NOTIFICATION: ", tradeupdate)
|
#print("FILL NOTIFICATION: ", tradeupdate)
|
||||||
res = asyncio.run(self.order_fill_callback(tradeupdate, tradeupdate.account))
|
res = asyncio.run(self.order_fill_callback(tradeupdate))
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def update_internal_account(self, o: Order):
|
def update_account(self, o: Order):
|
||||||
#updatujeme self.account
|
#updatujeme self.account
|
||||||
#pokud neexistuje klic v accountu vytvorime si ho
|
#pokud neexistuje klic v accountu vytvorime si ho
|
||||||
if o.symbol not in self.internal_account[o.account.name]:
|
if o.symbol not in self.account:
|
||||||
# { "BAC": [size, avgp] }
|
# { "BAC": [size, avgp] }
|
||||||
self.internal_account[o.account.name][o.symbol] = [0,0]
|
self.account[o.symbol] = [0,0]
|
||||||
|
|
||||||
if o.side == OrderSide.BUY:
|
if o.side == OrderSide.BUY:
|
||||||
#[size, avgp]
|
#[size, avgp]
|
||||||
newsize = (self.internal_account[o.account.name][o.symbol][0] + o.qty)
|
newsize = (self.account[o.symbol][0] + o.qty)
|
||||||
#JPLNE UZAVRENI SHORT (avgp 0)
|
#JPLNE UZAVRENI SHORT (avgp 0)
|
||||||
if newsize == 0: newavgp = 0
|
if newsize == 0: newavgp = 0
|
||||||
#CASTECNE UZAVRENI SHORT (avgp puvodni)
|
#CASTECNE UZAVRENI SHORT (avgp puvodni)
|
||||||
elif newsize < 0: newavgp = self.internal_account[o.account.name][o.symbol][1]
|
elif newsize < 0: newavgp = self.account[o.symbol][1]
|
||||||
#JDE O LONG (avgp nove)
|
#JDE O LONG (avgp nove)
|
||||||
else:
|
else:
|
||||||
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)
|
newavgp = ((self.account[o.symbol][0] * self.account[o.symbol][1]) + (o.qty * o.filled_avg_price)) / (self.account[o.symbol][0] + o.qty)
|
||||||
|
|
||||||
self.internal_account[o.account.name][o.symbol] = [newsize, newavgp]
|
self.account[o.symbol] = [newsize, newavgp]
|
||||||
self.cash = self.cash - (o.qty * o.filled_avg_price)
|
self.cash = self.cash - (o.qty * o.filled_avg_price)
|
||||||
return 1
|
return 1
|
||||||
#sell
|
#sell
|
||||||
elif o.side == OrderSide.SELL:
|
elif o.side == OrderSide.SELL:
|
||||||
newsize = self.internal_account[o.account.name][o.symbol][0]-o.qty
|
newsize = self.account[o.symbol][0]-o.qty
|
||||||
#UPLNE UZAVRENI LONGU (avgp 0)
|
#UPLNE UZAVRENI LONGU (avgp 0)
|
||||||
if newsize == 0: newavgp = 0
|
if newsize == 0: newavgp = 0
|
||||||
#CASTECNE UZAVRENI LONGU (avgp puvodni)
|
#CASTECNE UZAVRENI LONGU (avgp puvodni)
|
||||||
elif newsize > 0: newavgp = self.internal_account[o.account.name][o.symbol][1]
|
elif newsize > 0: newavgp = self.account[o.symbol][1]
|
||||||
#jde o SHORT (avgp nove)
|
#jde o SHORT (avgp nove)
|
||||||
else:
|
else:
|
||||||
#pokud je predchozi 0 - tzn. jde o prvni short
|
#pokud je predchozi 0 - tzn. jde o prvni short
|
||||||
if self.internal_account[o.account.name][o.symbol][1] == 0:
|
if self.account[o.symbol][1] == 0:
|
||||||
newavgp = o.filled_avg_price
|
newavgp = o.filled_avg_price
|
||||||
else:
|
else:
|
||||||
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)
|
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)
|
||||||
|
|
||||||
self.internal_account[o.account.name][o.symbol] = [newsize, newavgp]
|
self.account[o.symbol] = [newsize, newavgp]
|
||||||
|
|
||||||
#pokud jde o prodej longu(nova pozice je>=0) upravujeme cash
|
#pokud jde o prodej longu(nova pozice je>=0) upravujeme cash
|
||||||
if self.internal_account[o.account.name][o.symbol][0] >= 0:
|
if self.account[o.symbol][0] >= 0:
|
||||||
self.cash = float(self.cash + (o.qty * o.filled_avg_price))
|
self.cash = float(self.cash + (o.qty * o.filled_avg_price))
|
||||||
print("uprava cashe, jde o prodej longu")
|
print("uprava cashe, jde o prodej longu")
|
||||||
else:
|
else:
|
||||||
@ -485,7 +466,7 @@ class Backtester:
|
|||||||
# #ic("get last price")
|
# #ic("get last price")
|
||||||
# return self.btdata[i-1][1]
|
# return self.btdata[i-1][1]
|
||||||
|
|
||||||
def submit_order(self, time: float, symbol: str, side: OrderSide, size: int, order_type: OrderType, account: Account, price: float = None):
|
def submit_order(self, time: float, symbol: str, side: OrderSide, size: int, order_type: OrderType, price: float = None):
|
||||||
"""submit order
|
"""submit order
|
||||||
- zakladni validace
|
- zakladni validace
|
||||||
- vloží do self.open_orders s daným časem
|
- vloží do self.open_orders s daným časem
|
||||||
@ -518,9 +499,9 @@ class Backtester:
|
|||||||
return -1
|
return -1
|
||||||
|
|
||||||
#pokud neexistuje klic v accountu vytvorime si ho
|
#pokud neexistuje klic v accountu vytvorime si ho
|
||||||
if symbol not in self.internal_account[account.name]:
|
if symbol not in self.account:
|
||||||
# { "BAC": [size, avgp] }
|
# { "BAC": [size, avgp] }
|
||||||
self.internal_account[account.name][symbol] = [0,0]
|
self.account[symbol] = [0,0]
|
||||||
|
|
||||||
#check for available quantity
|
#check for available quantity
|
||||||
if side == OrderSide.SELL:
|
if side == OrderSide.SELL:
|
||||||
@ -528,15 +509,15 @@ class Backtester:
|
|||||||
reserved_price = 0
|
reserved_price = 0
|
||||||
#with lock:
|
#with lock:
|
||||||
for o in self.open_orders:
|
for o in self.open_orders:
|
||||||
if o.side == OrderSide.SELL and o.symbol == symbol and o.canceled_at is None and o.account==account:
|
if o.side == OrderSide.SELL and o.symbol == symbol and o.canceled_at is None:
|
||||||
reserved += o.qty
|
reserved += o.qty
|
||||||
cena = o.limit_price if o.limit_price else self.get_last_price(time, o.symbol)
|
cena = o.limit_price if o.limit_price else self.get_last_price(time, o.symbol)
|
||||||
reserved_price += o.qty * cena
|
reserved_price += o.qty * cena
|
||||||
print("blokovano v open orders pro sell qty: ", reserved, "celkem:", reserved_price)
|
print("blokovano v open orders pro sell qty: ", reserved, "celkem:", reserved_price)
|
||||||
|
|
||||||
actual_minus_reserved = int(self.internal_account[account.name][symbol][0]) - reserved
|
actual_minus_reserved = int(self.account[symbol][0]) - reserved
|
||||||
if actual_minus_reserved > 0 and actual_minus_reserved - int(size) < 0:
|
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.internal_account[account.name][symbol][0],"reserved",reserved,"available",int(self.internal_account[account.name][symbol][0]) - reserved,"selling",size)
|
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)
|
||||||
return -1
|
return -1
|
||||||
|
|
||||||
#if is shorting - check available cash to short
|
#if is shorting - check available cash to short
|
||||||
@ -552,13 +533,13 @@ class Backtester:
|
|||||||
reserved_price = 0
|
reserved_price = 0
|
||||||
#with lock:
|
#with lock:
|
||||||
for o in self.open_orders:
|
for o in self.open_orders:
|
||||||
if o.side == OrderSide.BUY and o.canceled_at is None and o.account==account:
|
if o.side == OrderSide.BUY and o.canceled_at is None:
|
||||||
cena = o.limit_price if o.limit_price else self.get_last_price(time, o.symbol)
|
cena = o.limit_price if o.limit_price else self.get_last_price(time, o.symbol)
|
||||||
reserved_price += o.qty * cena
|
reserved_price += o.qty * cena
|
||||||
reserved_qty += o.qty
|
reserved_qty += o.qty
|
||||||
print("blokovano v open orders for buy: qty, price", reserved_qty, reserved_price)
|
print("blokovano v open orders for buy: qty, price", reserved_qty, reserved_price)
|
||||||
|
|
||||||
actual_plus_reserved_qty = int(self.internal_account[account.name][symbol][0]) + reserved_qty
|
actual_plus_reserved_qty = int(self.account[symbol][0]) + reserved_qty
|
||||||
|
|
||||||
#jde o uzavreni shortu
|
#jde o uzavreni shortu
|
||||||
if actual_plus_reserved_qty < 0 and (actual_plus_reserved_qty + int(size)) > 0:
|
if actual_plus_reserved_qty < 0 and (actual_plus_reserved_qty + int(size)) > 0:
|
||||||
@ -574,7 +555,6 @@ class Backtester:
|
|||||||
|
|
||||||
id = str(uuid4())
|
id = str(uuid4())
|
||||||
order = Order(id=id,
|
order = Order(id=id,
|
||||||
account=account,
|
|
||||||
submitted_at = datetime.fromtimestamp(float(time)),
|
submitted_at = datetime.fromtimestamp(float(time)),
|
||||||
symbol=symbol,
|
symbol=symbol,
|
||||||
order_type = order_type,
|
order_type = order_type,
|
||||||
@ -589,7 +569,7 @@ class Backtester:
|
|||||||
return id
|
return id
|
||||||
|
|
||||||
|
|
||||||
def replace_order(self, id: str, time: float, account: Account, size: int = None, price: float = None):
|
def replace_order(self, id: str, time: float, size: int = None, price: float = None):
|
||||||
"""replace order
|
"""replace order
|
||||||
- zakladni validace vrací synchronně
|
- zakladni validace vrací synchronně
|
||||||
- vrací číslo nové objednávky
|
- vrací číslo nové objednávky
|
||||||
@ -606,7 +586,7 @@ class Backtester:
|
|||||||
#with lock:
|
#with lock:
|
||||||
for o in self.open_orders:
|
for o in self.open_orders:
|
||||||
print(o.id)
|
print(o.id)
|
||||||
if str(o.id) == str(id) and o.account == account:
|
if str(o.id) == str(id):
|
||||||
newid = str(uuid4())
|
newid = str(uuid4())
|
||||||
o.id = newid
|
o.id = newid
|
||||||
o.submitted_at = datetime.fromtimestamp(time)
|
o.submitted_at = datetime.fromtimestamp(time)
|
||||||
@ -617,7 +597,7 @@ class Backtester:
|
|||||||
print("BT: replacement order doesnt exist")
|
print("BT: replacement order doesnt exist")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def cancel_order(self, time: float, id: str, account: Account):
|
def cancel_order(self, time: float, id: str):
|
||||||
"""cancel order
|
"""cancel order
|
||||||
- základní validace vrací synchronně
|
- základní validace vrací synchronně
|
||||||
- vymaže objednávku z open orders
|
- vymaže objednávku z open orders
|
||||||
@ -633,22 +613,22 @@ class Backtester:
|
|||||||
return 0
|
return 0
|
||||||
#with lock:
|
#with lock:
|
||||||
for o in self.open_orders:
|
for o in self.open_orders:
|
||||||
if str(o.id) == id and o.account == account:
|
if str(o.id) == id:
|
||||||
o.canceled_at = time
|
o.canceled_at = time
|
||||||
print("set as canceled in self.open_orders")
|
print("set as canceled in self.open_orders")
|
||||||
return 1
|
return 1
|
||||||
print("BTC: cantchange. open order doesnt exist")
|
print("BTC: cantchange. open order doesnt exist")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
def get_open_position(self, symbol: str, account: Account):
|
def get_open_position(self, symbol: str):
|
||||||
"""get positions ->(avg,size)"""
|
"""get positions ->(avg,size)"""
|
||||||
#print("BT:get open positions entry")
|
#print("BT:get open positions entry")
|
||||||
try:
|
try:
|
||||||
return self.internal_account[account.name][symbol][1], self.internal_account[account.name][symbol][0]
|
return self.account[symbol][1], self.account[symbol][0]
|
||||||
except:
|
except:
|
||||||
return (0,0)
|
return (0,0)
|
||||||
|
|
||||||
def get_open_orders(self, side: OrderSide, symbol: str, account: Account):
|
def get_open_orders(self, side: OrderSide, symbol: str):
|
||||||
"""get open orders ->list(OrderNotification)"""
|
"""get open orders ->list(OrderNotification)"""
|
||||||
print("BT:get open orders entry")
|
print("BT:get open orders entry")
|
||||||
if len(self.open_orders) == 0:
|
if len(self.open_orders) == 0:
|
||||||
@ -658,7 +638,7 @@ class Backtester:
|
|||||||
#with lock:
|
#with lock:
|
||||||
for o in self.open_orders:
|
for o in self.open_orders:
|
||||||
#print(o)
|
#print(o)
|
||||||
if o.symbol == symbol and o.canceled_at is None and o.account == account:
|
if o.symbol == symbol and o.canceled_at is None:
|
||||||
if side is None or o.side == side:
|
if side is None or o.side == side:
|
||||||
res.append(o)
|
res.append(o)
|
||||||
return res
|
return res
|
||||||
|
|||||||
41
v2realbot/common/PrescribedTradeModel.py
Normal file
41
v2realbot/common/PrescribedTradeModel.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
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,75 +5,10 @@ from rich import print
|
|||||||
from typing import Any, Optional, List, Union
|
from typing import Any, Optional, List, Union
|
||||||
from datetime import datetime, date
|
from datetime import datetime, date
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from v2realbot.enums.enums import Mode, Account, SchedulerStatus, Moddus, Market, Followup
|
from v2realbot.enums.enums import Mode, Account, SchedulerStatus, Moddus, Market
|
||||||
from alpaca.data.enums import Exchange
|
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
|
#models for server side datatables
|
||||||
@ -156,7 +91,7 @@ class TestList(BaseModel):
|
|||||||
dates: List[Intervals]
|
dates: List[Intervals]
|
||||||
|
|
||||||
#for GUI to fetch historical trades on given symbol
|
#for GUI to fetch historical trades on given symbol
|
||||||
class TradeView(BaseModel):
|
class Trade(BaseModel):
|
||||||
symbol: str
|
symbol: str
|
||||||
timestamp: datetime
|
timestamp: datetime
|
||||||
exchange: Optional[Union[Exchange, str]] = None
|
exchange: Optional[Union[Exchange, str]] = None
|
||||||
@ -254,8 +189,8 @@ class RunnerView(BaseModel):
|
|||||||
run_symbol: Optional[str] = None
|
run_symbol: Optional[str] = None
|
||||||
run_trade_count: Optional[int] = 0
|
run_trade_count: Optional[int] = 0
|
||||||
run_profit: Optional[float] = 0
|
run_profit: Optional[float] = 0
|
||||||
run_positions: Optional[dict] = 0
|
run_positions: Optional[int] = 0
|
||||||
run_avgp: Optional[dict] = 0
|
run_avgp: Optional[float] = 0
|
||||||
run_stopped: Optional[datetime] = None
|
run_stopped: Optional[datetime] = None
|
||||||
run_paused: Optional[datetime] = None
|
run_paused: Optional[datetime] = None
|
||||||
|
|
||||||
@ -273,8 +208,8 @@ class Runner(BaseModel):
|
|||||||
run_ilog_save: Optional[bool] = False
|
run_ilog_save: Optional[bool] = False
|
||||||
run_trade_count: Optional[int] = None
|
run_trade_count: Optional[int] = None
|
||||||
run_profit: Optional[float] = None
|
run_profit: Optional[float] = None
|
||||||
run_positions: Optional[dict] = None
|
run_positions: Optional[int] = None
|
||||||
run_avgp: Optional[dict] = None
|
run_avgp: Optional[float] = None
|
||||||
run_strat_json: Optional[str] = None
|
run_strat_json: Optional[str] = None
|
||||||
run_stopped: Optional[datetime] = None
|
run_stopped: Optional[datetime] = None
|
||||||
run_paused: Optional[datetime] = None
|
run_paused: Optional[datetime] = None
|
||||||
@ -312,7 +247,6 @@ class Bar(BaseModel):
|
|||||||
vwap: Optional[float] = 0
|
vwap: Optional[float] = 0
|
||||||
|
|
||||||
class Order(BaseModel):
|
class Order(BaseModel):
|
||||||
account: Account
|
|
||||||
id: UUID
|
id: UUID
|
||||||
submitted_at: datetime
|
submitted_at: datetime
|
||||||
filled_at: Optional[datetime] = None
|
filled_at: Optional[datetime] = None
|
||||||
@ -328,7 +262,6 @@ class Order(BaseModel):
|
|||||||
|
|
||||||
#entita pro kazdy kompletni FILL, je navazana na prescribed_trade
|
#entita pro kazdy kompletni FILL, je navazana na prescribed_trade
|
||||||
class TradeUpdate(BaseModel):
|
class TradeUpdate(BaseModel):
|
||||||
account: Account
|
|
||||||
event: Union[TradeEvent, str]
|
event: Union[TradeEvent, str]
|
||||||
execution_id: Optional[UUID] = None
|
execution_id: Optional[UUID] = None
|
||||||
order: Order
|
order: Order
|
||||||
@ -374,8 +307,8 @@ class RunArchive(BaseModel):
|
|||||||
ilog_save: Optional[bool] = False
|
ilog_save: Optional[bool] = False
|
||||||
profit: float = 0
|
profit: float = 0
|
||||||
trade_count: int = 0
|
trade_count: int = 0
|
||||||
end_positions: Union[dict,str] = None
|
end_positions: int = 0
|
||||||
end_positions_avgp: Union[dict,str] = None
|
end_positions_avgp: float = 0
|
||||||
metrics: Union[dict, str] = None
|
metrics: Union[dict, str] = None
|
||||||
stratvars_toml: Optional[str] = None
|
stratvars_toml: Optional[str] = None
|
||||||
|
|
||||||
@ -396,8 +329,8 @@ class RunArchiveView(BaseModel):
|
|||||||
ilog_save: Optional[bool] = False
|
ilog_save: Optional[bool] = False
|
||||||
profit: float = 0
|
profit: float = 0
|
||||||
trade_count: int = 0
|
trade_count: int = 0
|
||||||
end_positions: Union[dict,int] = None
|
end_positions: int = 0
|
||||||
end_positions_avgp: Union[dict,float] = None
|
end_positions_avgp: float = 0
|
||||||
metrics: Union[dict, str] = None
|
metrics: Union[dict, str] = None
|
||||||
batch_profit: float = 0 # Total profit for the batch - now calculated during query
|
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
|
batch_count: int = 0 # Count of runs in the batch - now calculated during query
|
||||||
@ -414,8 +347,6 @@ class SLHistory(BaseModel):
|
|||||||
id: Optional[UUID] = None
|
id: Optional[UUID] = None
|
||||||
time: datetime
|
time: datetime
|
||||||
sl_val: float
|
sl_val: float
|
||||||
direction: TradeDirection
|
|
||||||
account: Account
|
|
||||||
|
|
||||||
#Contains archive of running strategies (runner) - detail data
|
#Contains archive of running strategies (runner) - detail data
|
||||||
class RunArchiveDetail(BaseModel):
|
class RunArchiveDetail(BaseModel):
|
||||||
@ -428,3 +359,9 @@ class RunArchiveDetail(BaseModel):
|
|||||||
trades: List[TradeUpdate]
|
trades: List[TradeUpdate]
|
||||||
ext_data: Optional[dict] = None
|
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']),
|
ilog_save=bool(row['ilog_save']),
|
||||||
profit=float(row['profit']),
|
profit=float(row['profit']),
|
||||||
trade_count=int(row['trade_count']),
|
trade_count=int(row['trade_count']),
|
||||||
end_positions=orjson.loads(row['end_positions']),
|
end_positions=int(row['end_positions']),
|
||||||
end_positions_avgp=orjson.loads(row['end_positions_avgp']),
|
end_positions_avgp=float(row['end_positions_avgp']),
|
||||||
metrics=orjson.loads(row['metrics']) if row['metrics'] else None,
|
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_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,
|
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']),
|
ilog_save=bool(row['ilog_save']),
|
||||||
profit=float(row['profit']),
|
profit=float(row['profit']),
|
||||||
trade_count=int(row['trade_count']),
|
trade_count=int(row['trade_count']),
|
||||||
end_positions=str(row['end_positions']),
|
end_positions=int(row['end_positions']),
|
||||||
end_positions_avgp=str(row['end_positions_avgp']),
|
end_positions_avgp=float(row['end_positions_avgp']),
|
||||||
metrics=orjson.loads(row['metrics']),
|
metrics=orjson.loads(row['metrics']),
|
||||||
stratvars_toml=row['stratvars_toml'],
|
stratvars_toml=row['stratvars_toml'],
|
||||||
transferables=orjson.loads(row['transferables']) if row['transferables'] else None
|
transferables=orjson.loads(row['transferables']) if row['transferables'] else None
|
||||||
|
|||||||
@ -11,12 +11,16 @@ _ml_module_loaded = False
|
|||||||
|
|
||||||
#directory for generated images and basic reports
|
#directory for generated images and basic reports
|
||||||
MEDIA_DIRECTORY = Path(__file__).parent.parent.parent / "media"
|
MEDIA_DIRECTORY = Path(__file__).parent.parent.parent / "media"
|
||||||
|
VBT_DOC_DIRECTORY = Path(__file__).parent.parent.parent / "vbt-doc" #directory for vbt doc
|
||||||
RUNNER_DETAIL_DIRECTORY = Path(__file__).parent.parent.parent / "runner_detail"
|
RUNNER_DETAIL_DIRECTORY = Path(__file__).parent.parent.parent / "runner_detail"
|
||||||
|
|
||||||
#location of strat.log - it is used to fetch by gui
|
#location of strat.log - it is used to fetch by gui
|
||||||
LOG_PATH = Path(__file__).parent.parent
|
LOG_PATH = Path(__file__).parent.parent
|
||||||
LOG_FILE = Path(__file__).parent.parent / "strat.log"
|
LOG_FILE = Path(__file__).parent.parent / "strat.log"
|
||||||
JOB_LOG_FILE = Path(__file__).parent.parent / "job.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 that cannot be changed in gui
|
||||||
STRATVARS_UNCHANGEABLES = ['pendingbuys', 'blockbuy', 'jevylozeno', 'limitka']
|
STRATVARS_UNCHANGEABLES = ['pendingbuys', 'blockbuy', 'jevylozeno', 'limitka']
|
||||||
@ -27,26 +31,6 @@ MODEL_DIR = Path(DATA_DIR)/"models"
|
|||||||
PROFILING_NEXT_ENABLED = False
|
PROFILING_NEXT_ENABLED = False
|
||||||
PROFILING_OUTPUT_DIR = DATA_DIR
|
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
|
#NALOADUJEME DOTENV ENV VARIABLES
|
||||||
if load_dotenv(ENV_FILE, verbose=True) is False:
|
if load_dotenv(ENV_FILE, verbose=True) is False:
|
||||||
print(f"Error loading.env file {ENV_FILE}. Now depending on ENV VARIABLES set externally.")
|
print(f"Error loading.env file {ENV_FILE}. Now depending on ENV VARIABLES set externally.")
|
||||||
@ -83,10 +67,10 @@ def get_key(mode: Mode, account: Account):
|
|||||||
return None
|
return None
|
||||||
dict = globals()
|
dict = globals()
|
||||||
try:
|
try:
|
||||||
API_KEY = dict[str.upper(str(account.name)) + "_" + str.upper(str(mode.name)) + "_API_KEY" ]
|
API_KEY = dict[str.upper(str(account.value)) + "_" + str.upper(str(mode.value)) + "_API_KEY" ]
|
||||||
SECRET_KEY = dict[str.upper(str(account.name)) + "_" + str.upper(str(mode.name)) + "_SECRET_KEY" ]
|
SECRET_KEY = dict[str.upper(str(account.value)) + "_" + str.upper(str(mode.value)) + "_SECRET_KEY" ]
|
||||||
PAPER = dict[str.upper(str(account.name)) + "_" + str.upper(str(mode.name)) + "_PAPER" ]
|
PAPER = dict[str.upper(str(account.value)) + "_" + str.upper(str(mode.value)) + "_PAPER" ]
|
||||||
FEED = dict[str.upper(str(account.name)) + "_" + str.upper(str(mode.name)) + "_FEED" ]
|
FEED = dict[str.upper(str(account.value)) + "_" + str.upper(str(mode.value)) + "_FEED" ]
|
||||||
return Keys(API_KEY, SECRET_KEY, PAPER, FEED)
|
return Keys(API_KEY, SECRET_KEY, PAPER, FEED)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
print("Not valid combination to get keys for", mode, account)
|
print("Not valid combination to get keys for", mode, account)
|
||||||
@ -110,7 +94,7 @@ data_feed_type_str = os.environ.get('ACCOUNT1_PAPER_FEED', 'iex') # Default to
|
|||||||
# Convert the string to DataFeed enum
|
# Convert the string to DataFeed enum
|
||||||
try:
|
try:
|
||||||
ACCOUNT1_PAPER_FEED = DataFeed(data_feed_type_str)
|
ACCOUNT1_PAPER_FEED = DataFeed(data_feed_type_str)
|
||||||
except nameError:
|
except ValueError:
|
||||||
# Handle the case where the environment variable does not match any enum member
|
# 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'")
|
print(f"Invalid data feed type: {data_feed_type_str} in ACCOUNT1_PAPER_FEED defaulting to 'iex'")
|
||||||
ACCOUNT1_PAPER_FEED = DataFeed.SIP
|
ACCOUNT1_PAPER_FEED = DataFeed.SIP
|
||||||
@ -128,7 +112,7 @@ data_feed_type_str = os.environ.get('ACCOUNT1_LIVE_FEED', 'iex') # Default to '
|
|||||||
# Convert the string to DataFeed enum
|
# Convert the string to DataFeed enum
|
||||||
try:
|
try:
|
||||||
ACCOUNT1_LIVE_FEED = DataFeed(data_feed_type_str)
|
ACCOUNT1_LIVE_FEED = DataFeed(data_feed_type_str)
|
||||||
except nameError:
|
except ValueError:
|
||||||
# Handle the case where the environment variable does not match any enum member
|
# 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'")
|
print(f"Invalid data feed type: {data_feed_type_str} in ACCOUNT1_LIVE_FEED defaulting to 'iex'")
|
||||||
ACCOUNT1_LIVE_FEED = DataFeed.IEX
|
ACCOUNT1_LIVE_FEED = DataFeed.IEX
|
||||||
@ -146,7 +130,7 @@ data_feed_type_str = os.environ.get('ACCOUNT2_PAPER_FEED', 'iex') # Default to
|
|||||||
# Convert the string to DataFeed enum
|
# Convert the string to DataFeed enum
|
||||||
try:
|
try:
|
||||||
ACCOUNT2_PAPER_FEED = DataFeed(data_feed_type_str)
|
ACCOUNT2_PAPER_FEED = DataFeed(data_feed_type_str)
|
||||||
except nameError:
|
except ValueError:
|
||||||
# Handle the case where the environment variable does not match any enum member
|
# 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'")
|
print(f"Invalid data feed type: {data_feed_type_str} in ACCOUNT2_PAPER_FEED defaulting to 'iex'")
|
||||||
ACCOUNT2_PAPER_FEED = DataFeed.IEX
|
ACCOUNT2_PAPER_FEED = DataFeed.IEX
|
||||||
@ -165,7 +149,7 @@ except nameError:
|
|||||||
# # Convert the string to DataFeed enum
|
# # Convert the string to DataFeed enum
|
||||||
# try:
|
# try:
|
||||||
# ACCOUNT2_LIVE_FEED = DataFeed(data_feed_type_str)
|
# ACCOUNT2_LIVE_FEED = DataFeed(data_feed_type_str)
|
||||||
# except nameError:
|
# except ValueError:
|
||||||
# # Handle the case where the environment variable does not match any enum member
|
# # 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'")
|
# print(f"Invalid data feed type: {data_feed_type_str} in ACCOUNT2_LIVE_FEED defaulting to 'iex'")
|
||||||
# ACCOUNT2_LIVE_FEED = DataFeed.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.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.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.utils.ilog import delete_logs
|
||||||
from v2realbot.common.model import Trade, TradeDirection, TradeStatus, TradeStoplossType
|
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from v2realbot.loader.trade_offline_streamer import Trade_Offline_Streamer
|
from v2realbot.loader.trade_offline_streamer import Trade_Offline_Streamer
|
||||||
from threading import Thread, current_thread, Event, enumerate
|
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.strategy.base import StrategyState
|
||||||
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, OrderSide
|
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.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, gaka
|
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.ilog import delete_logs
|
from v2realbot.utils.ilog import delete_logs
|
||||||
from v2realbot.common.model import Trade, TradeDirection, TradeStatus, TradeStoplossType
|
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from v2realbot.loader.trade_offline_streamer import Trade_Offline_Streamer
|
from v2realbot.loader.trade_offline_streamer import Trade_Offline_Streamer
|
||||||
from threading import Thread, current_thread, Event, enumerate
|
from threading import Thread, current_thread, Event, enumerate
|
||||||
@ -71,8 +71,8 @@ def get_all_runners():
|
|||||||
if i.run_instance:
|
if i.run_instance:
|
||||||
i.run_profit = round(float(i.run_instance.state.profit),2)
|
i.run_profit = round(float(i.run_instance.state.profit),2)
|
||||||
i.run_trade_count = len(i.run_instance.state.tradeList)
|
i.run_trade_count = len(i.run_instance.state.tradeList)
|
||||||
i.run_positions = gaka(i.run_instance.state.account_variables, "positions")
|
i.run_positions = i.run_instance.state.positions
|
||||||
i.run_avgp = gaka(i.run_instance.state.account_variables, "avgp", lambda x: round(float(x),3))
|
i.run_avgp = round(float(i.run_instance.state.avgp),3)
|
||||||
return (0, db.runners)
|
return (0, db.runners)
|
||||||
else:
|
else:
|
||||||
return (0, [])
|
return (0, [])
|
||||||
@ -94,8 +94,8 @@ def get_runner(id: UUID):
|
|||||||
if str(i.id) == str(id):
|
if str(i.id) == str(id):
|
||||||
i.run_profit = round(float(i.run_instance.state.profit),2)
|
i.run_profit = round(float(i.run_instance.state.profit),2)
|
||||||
i.run_trade_count = len(i.run_instance.state.tradeList)
|
i.run_trade_count = len(i.run_instance.state.tradeList)
|
||||||
i.run_positions =gaka(i.run_instance.state.account_variables, "positions")
|
i.run_positions = i.run_instance.state.positions
|
||||||
i.run_avgp = gaka(i.run_instance.state.account_variables, "avgp", lambda x: round(float(x),3))
|
i.run_avgp = round(float(i.run_instance.state.avgp),3)
|
||||||
return (0, i)
|
return (0, i)
|
||||||
return (-2, "not found")
|
return (-2, "not found")
|
||||||
|
|
||||||
@ -738,14 +738,13 @@ def populate_metrics_output_directory(strat: StrategyInstance, inter_batch_param
|
|||||||
|
|
||||||
tradeList = strat.state.tradeList
|
tradeList = strat.state.tradeList
|
||||||
|
|
||||||
trade_dict = AttributeDict(account=[],orderid=[],timestamp=[],symbol=[],side=[],order_type=[],qty=[],price=[],position_qty=[])
|
trade_dict = AttributeDict(orderid=[],timestamp=[],symbol=[],side=[],order_type=[],qty=[],price=[],position_qty=[])
|
||||||
if strat.mode == Mode.BT:
|
if strat.mode == Mode.BT:
|
||||||
trade_dict["value"] = []
|
trade_dict["value"] = []
|
||||||
trade_dict["cash"] = []
|
trade_dict["cash"] = []
|
||||||
trade_dict["pos_avg_price"] = []
|
trade_dict["pos_avg_price"] = []
|
||||||
for t in tradeList:
|
for t in tradeList:
|
||||||
if t.event == TradeEvent.FILL:
|
if t.event == TradeEvent.FILL:
|
||||||
trade_dict.account.append(t.account)
|
|
||||||
trade_dict.orderid.append(str(t.order.id))
|
trade_dict.orderid.append(str(t.order.id))
|
||||||
trade_dict.timestamp.append(t.timestamp)
|
trade_dict.timestamp.append(t.timestamp)
|
||||||
trade_dict.symbol.append(t.order.symbol)
|
trade_dict.symbol.append(t.order.symbol)
|
||||||
@ -769,12 +768,10 @@ def populate_metrics_output_directory(strat: StrategyInstance, inter_batch_param
|
|||||||
max_positions = max_positions[max_positions['side'] == OrderSide.SELL]
|
max_positions = max_positions[max_positions['side'] == OrderSide.SELL]
|
||||||
max_positions = max_positions.drop(columns=['side'], axis=1)
|
max_positions = max_positions.drop(columns=['side'], axis=1)
|
||||||
|
|
||||||
res = dict(account_variables={}, profit={})
|
res = dict(profit={})
|
||||||
#filt = max_positions['side'] == 'OrderSide.BUY'
|
#filt = max_positions['side'] == 'OrderSide.BUY'
|
||||||
|
|
||||||
res["account_variables"] = transform_data(strat.state.account_variables, json_serial)
|
|
||||||
|
|
||||||
res["pos_cnt"] = dict(zip(str(max_positions['qty']), max_positions['count']))
|
res["pos_cnt"] = dict(zip(str(max_positions['qty']), max_positions['count']))
|
||||||
|
|
||||||
#naplneni batch sum profitu
|
#naplneni batch sum profitu
|
||||||
if inter_batch_params is not None:
|
if inter_batch_params is not None:
|
||||||
res["profit"]["batch_sum_profit"] = int(inter_batch_params["batch_profit"])
|
res["profit"]["batch_sum_profit"] = int(inter_batch_params["batch_profit"])
|
||||||
@ -926,8 +923,8 @@ def archive_runner(runner: Runner, strat: StrategyInstance, inter_batch_params:
|
|||||||
settings = settings,
|
settings = settings,
|
||||||
profit=round(float(strat.state.profit),2),
|
profit=round(float(strat.state.profit),2),
|
||||||
trade_count=len(strat.state.tradeList),
|
trade_count=len(strat.state.tradeList),
|
||||||
end_positions=gaka(strat.state.account_variables, "positions"),
|
end_positions=strat.state.positions,
|
||||||
end_positions_avgp=gaka(strat.state.account_variables, "avgp", lambda x: round(float(x),3)),
|
end_positions_avgp=round(float(strat.state.avgp),3),
|
||||||
metrics=results_metrics,
|
metrics=results_metrics,
|
||||||
stratvars_toml=runner.run_stratvars_toml,
|
stratvars_toml=runner.run_stratvars_toml,
|
||||||
transferables=strat.state.vars["transferables"]
|
transferables=strat.state.vars["transferables"]
|
||||||
@ -1267,7 +1264,6 @@ def insert_archive_header(archeader: RunArchive):
|
|||||||
try:
|
try:
|
||||||
c = conn.cursor()
|
c = conn.cursor()
|
||||||
#json_string = orjson.dumps(archeader, default=json_serial, option=orjson.OPT_PASSTHROUGH_DATETIME)
|
#json_string = orjson.dumps(archeader, default=json_serial, option=orjson.OPT_PASSTHROUGH_DATETIME)
|
||||||
#print(archeader)
|
|
||||||
|
|
||||||
res = c.execute("""
|
res = c.execute("""
|
||||||
INSERT INTO runner_header
|
INSERT INTO runner_header
|
||||||
@ -1275,7 +1271,7 @@ def insert_archive_header(archeader: RunArchive):
|
|||||||
VALUES
|
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, 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')))
|
(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')))
|
||||||
|
|
||||||
#retry not yet supported for statement format above
|
#retry not yet supported for statement format above
|
||||||
#res = execute_with_retry(c,statement)
|
#res = execute_with_retry(c,statement)
|
||||||
@ -1650,14 +1646,13 @@ def preview_indicator_byTOML(id: UUID, indicator: InstantIndicator, save: bool =
|
|||||||
new_inds = AttributeDict(**new_inds)
|
new_inds = AttributeDict(**new_inds)
|
||||||
new_tick_inds = {key: [] for key in detail.indicators[1].keys()}
|
new_tick_inds = {key: [] for key in detail.indicators[1].keys()}
|
||||||
new_tick_inds = AttributeDict(**new_tick_inds)
|
new_tick_inds = AttributeDict(**new_tick_inds)
|
||||||
def_account = Account("ACCOUNT1")
|
interface = BacktestInterface(symbol="X", bt=None)
|
||||||
interface = BacktestInterface(symbol="X", bt=None, account=def_account)
|
|
||||||
|
|
||||||
##dame nastaveni indikatoru do tvaru, ktery stratvars ocekava (pro dynmaicke inicializace)
|
##dame nastaveni indikatoru do tvaru, ktery stratvars ocekava (pro dynmaicke inicializace)
|
||||||
stratvars = AttributeDict(indicators=AttributeDict(**{jmeno:toml_parsed}))
|
stratvars = AttributeDict(indicators=AttributeDict(**{jmeno:toml_parsed}))
|
||||||
#print("stratvars", stratvars)
|
#print("stratvars", stratvars)
|
||||||
|
|
||||||
state = StrategyState(name="XX", symbol = "X", stratvars = AttributeDict(**stratvars), interface=interface, accounts=[def_account], account=def_account)
|
state = StrategyState(name="XX", symbol = "X", stratvars = AttributeDict(**stratvars), interface=interface)
|
||||||
|
|
||||||
#inicializujeme stavove promenne a novy indikator v cilovem dict
|
#inicializujeme stavove promenne a novy indikator v cilovem dict
|
||||||
if output == "bar":
|
if output == "bar":
|
||||||
|
|||||||
@ -5,7 +5,6 @@ from v2realbot.backtesting.backtester import Backtester
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from v2realbot.utils.utils import zoneNY
|
from v2realbot.utils.utils import zoneNY
|
||||||
import v2realbot.utils.config_handler as cfh
|
import v2realbot.utils.config_handler as cfh
|
||||||
from v2realbot.common.model import Account
|
|
||||||
|
|
||||||
""""
|
""""
|
||||||
backtester methods can be called
|
backtester methods can be called
|
||||||
@ -17,9 +16,8 @@ both should be backtestable
|
|||||||
if method are called for the past self.time must be set accordingly
|
if method are called for the past self.time must be set accordingly
|
||||||
"""
|
"""
|
||||||
class BacktestInterface(GeneralInterface):
|
class BacktestInterface(GeneralInterface):
|
||||||
def __init__(self, symbol, bt: Backtester, account: Account) -> None:
|
def __init__(self, symbol, bt: Backtester) -> None:
|
||||||
self.symbol = symbol
|
self.symbol = symbol
|
||||||
self.account = account
|
|
||||||
self.bt = bt
|
self.bt = bt
|
||||||
self.count_api_requests = cfh.config_handler.get_val('COUNT_API_REQUESTS')
|
self.count_api_requests = cfh.config_handler.get_val('COUNT_API_REQUESTS')
|
||||||
self.mincnt = list([dict(minute=0,count=0)])
|
self.mincnt = list([dict(minute=0,count=0)])
|
||||||
@ -45,48 +43,48 @@ class BacktestInterface(GeneralInterface):
|
|||||||
def buy(self, size = 1, repeat: bool = False):
|
def buy(self, size = 1, repeat: bool = False):
|
||||||
self.count()
|
self.count()
|
||||||
#add REST API latency
|
#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, account=self.account)
|
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)
|
||||||
|
|
||||||
"""buy limit"""
|
"""buy limit"""
|
||||||
def buy_l(self, price: float, size: int = 1, repeat: bool = False, force: int = 0):
|
def buy_l(self, price: float, size: int = 1, repeat: bool = False, force: int = 0):
|
||||||
self.count()
|
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, account=self.account)
|
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)
|
||||||
|
|
||||||
"""sell market"""
|
"""sell market"""
|
||||||
def sell(self, size = 1, repeat: bool = False):
|
def sell(self, size = 1, repeat: bool = False):
|
||||||
self.count()
|
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, account=self.account)
|
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)
|
||||||
|
|
||||||
"""sell limit"""
|
"""sell limit"""
|
||||||
async def sell_l(self, price: float, size = 1, repeat: bool = False):
|
async def sell_l(self, price: float, size = 1, repeat: bool = False):
|
||||||
self.count()
|
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, account=self.account)
|
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)
|
||||||
|
|
||||||
"""replace order"""
|
"""replace order"""
|
||||||
async def repl(self, orderid: str, price: float = None, size: int = None, repeat: bool = False):
|
async def repl(self, orderid: str, price: float = None, size: int = None, repeat: bool = False):
|
||||||
self.count()
|
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, account=self.account)
|
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)
|
||||||
|
|
||||||
"""cancel order"""
|
"""cancel order"""
|
||||||
#TBD exec predtim?
|
#TBD exec predtim?
|
||||||
def cancel(self, orderid: str):
|
def cancel(self, orderid: str):
|
||||||
self.count()
|
self.count()
|
||||||
return self.bt.cancel_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'), id=orderid, account=self.account)
|
return self.bt.cancel_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'), id=orderid)
|
||||||
|
|
||||||
"""get positions ->(size,avgp)"""
|
"""get positions ->(size,avgp)"""
|
||||||
#TBD exec predtim?
|
#TBD exec predtim?
|
||||||
def pos(self):
|
def pos(self):
|
||||||
self.count()
|
self.count()
|
||||||
return self.bt.get_open_position(symbol=self.symbol, account=self.account)
|
return self.bt.get_open_position(symbol=self.symbol)
|
||||||
|
|
||||||
"""get open orders ->list(Order)"""
|
"""get open orders ->list(Order)"""
|
||||||
def get_open_orders(self, side: OrderSide, symbol: str):
|
def get_open_orders(self, side: OrderSide, symbol: str):
|
||||||
self.count()
|
self.count()
|
||||||
return self.bt.get_open_orders(side=side, symbol=symbol, account=self.account)
|
return self.bt.get_open_orders(side=side, symbol=symbol)
|
||||||
|
|
||||||
def get_last_price(self, symbol: str):
|
def get_last_price(self, symbol: str):
|
||||||
self.count()
|
self.count()
|
||||||
return self.bt.get_last_price(time=self.bt.time, account=self.account)
|
return self.bt.get_last_price(time=self.bt.time)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -97,7 +97,7 @@ class LiveInterface(GeneralInterface):
|
|||||||
return -1
|
return -1
|
||||||
|
|
||||||
"""sell limit"""
|
"""sell limit"""
|
||||||
def sell_l(self, price: float, size = 1, repeat: bool = False):
|
async def sell_l(self, price: float, size = 1, repeat: bool = False):
|
||||||
self.size = size
|
self.size = size
|
||||||
self.repeat = repeat
|
self.repeat = repeat
|
||||||
|
|
||||||
@ -124,7 +124,7 @@ class LiveInterface(GeneralInterface):
|
|||||||
return -1
|
return -1
|
||||||
|
|
||||||
"""order replace"""
|
"""order replace"""
|
||||||
def repl(self, orderid: str, price: float = None, size: int = None, repeatl: bool = False):
|
async def repl(self, orderid: str, price: float = None, size: int = None, repeatl: bool = False):
|
||||||
|
|
||||||
if not price and not size:
|
if not price and not size:
|
||||||
print("price or size has to be filled")
|
print("price or size has to be filled")
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
from threading import Thread, current_thread
|
from threading import Thread
|
||||||
from alpaca.trading.stream import TradingStream
|
from alpaca.trading.stream import TradingStream
|
||||||
from v2realbot.config import Keys
|
from v2realbot.config import Keys
|
||||||
from v2realbot.common.model import Account
|
|
||||||
|
|
||||||
#jelikoz Alpaca podporuje pripojeni libovolneho poctu websocket instanci na order updates
|
#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 ..)
|
#vytvorime pro kazdou bezici instanci vlastni webservisu (jinak bychom museli delat instanci pro kombinaci ACCOUNT1 - LIVE, ACCOUNT1 - PAPER, ACCOUNT2 - PAPER ..)
|
||||||
@ -15,16 +14,15 @@ As Alpaca supports connecting of any number of trade updates clients
|
|||||||
new instance of this websocket thread is created for each strategy instance.
|
new instance of this websocket thread is created for each strategy instance.
|
||||||
"""""
|
"""""
|
||||||
class LiveOrderUpdatesStreamer(Thread):
|
class LiveOrderUpdatesStreamer(Thread):
|
||||||
def __init__(self, key: Keys, name: str, account: Account) -> None:
|
def __init__(self, key: Keys, name: str) -> None:
|
||||||
self.key = key
|
self.key = key
|
||||||
self.account = account
|
|
||||||
self.strategy = None
|
self.strategy = None
|
||||||
self.client = TradingStream(api_key=key.API_KEY, secret_key=key.SECRET_KEY, paper=key.PAPER)
|
self.client = TradingStream(api_key=key.API_KEY, secret_key=key.SECRET_KEY, paper=key.PAPER)
|
||||||
Thread.__init__(self, name=name)
|
Thread.__init__(self, name=name)
|
||||||
|
|
||||||
#notif dispatcher - pouze 1 strategie
|
#notif dispatcher - pouze 1 strategie
|
||||||
async def distributor(self,data):
|
async def distributor(self,data):
|
||||||
if self.strategy.symbol == data.order.symbol: await self.strategy.order_updates(data, self.account)
|
if self.strategy.symbol == data.order.symbol: await self.strategy.order_updates(data)
|
||||||
|
|
||||||
# connects callback to interface object - responses for given symbol are routed to interface callback
|
# connects callback to interface object - responses for given symbol are routed to interface callback
|
||||||
def connect_callback(self, st):
|
def connect_callback(self, st):
|
||||||
@ -41,6 +39,6 @@ class LiveOrderUpdatesStreamer(Thread):
|
|||||||
print("connect strategy first")
|
print("connect strategy first")
|
||||||
return
|
return
|
||||||
self.client.subscribe_trade_updates(self.distributor)
|
self.client.subscribe_trade_updates(self.distributor)
|
||||||
print("*"*10, "WS Order Update Streamer started for", current_thread().name,"*"*10)
|
print("*"*10, "WS Order Update Streamer started for", self.strategy.name, "*"*10)
|
||||||
self.client.run()
|
self.client.run()
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import os,sys
|
import os,sys
|
||||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
os.environ["KERAS_BACKEND"] = "jax"
|
os.environ["KERAS_BACKEND"] = "jax"
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY, LOG_PATH, MODEL_DIR
|
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY, LOG_PATH, MODEL_DIR, VBT_DOC_DIRECTORY
|
||||||
from alpaca.data.timeframe import TimeFrame, TimeFrameUnit
|
from alpaca.data.timeframe import TimeFrame, TimeFrameUnit
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from rich import print
|
from rich import print
|
||||||
@ -10,7 +10,7 @@ from fastapi.security import APIKeyHeader
|
|||||||
import uvicorn
|
import uvicorn
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
from v2realbot.utils.ilog import get_log_window
|
from v2realbot.utils.ilog import get_log_window
|
||||||
from v2realbot.common.model import RunManagerRecord, StrategyInstance, RunnerView, RunRequest, TradeView, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, Bar, RunArchiveChange, TestList, ConfigItem, InstantIndicator, DataTablesRequest, AnalyzerInputs
|
from v2realbot.common.model import RunManagerRecord, StrategyInstance, RunnerView, RunRequest, Trade, 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 import FastAPI, WebSocket, WebSocketDisconnect, Depends, HTTPException, status, WebSocketException, Cookie, Query, Request
|
||||||
from fastapi.responses import FileResponse, StreamingResponse, JSONResponse
|
from fastapi.responses import FileResponse, StreamingResponse, JSONResponse
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
@ -122,6 +122,22 @@ async def static_files(request: Request, path: str, authenticated: bool = Depend
|
|||||||
|
|
||||||
return FileResponse(file_path)
|
return FileResponse(file_path)
|
||||||
|
|
||||||
|
@app.get("/vbt-doc/{file_path:path}")
|
||||||
|
async def serve_protected_docs(file_path: str, credentials: HTTPBasicCredentials = Depends(authenticate_user)):
|
||||||
|
file_location = VBT_DOC_DIRECTORY / file_path
|
||||||
|
|
||||||
|
if file_location.is_dir(): # If it's a directory, serve index.html
|
||||||
|
index_file = file_location / "index.html"
|
||||||
|
if index_file.exists():
|
||||||
|
return FileResponse(index_file)
|
||||||
|
else:
|
||||||
|
raise HTTPException(status_code=404, detail="Index file not found")
|
||||||
|
elif file_location.exists():
|
||||||
|
return FileResponse(file_location)
|
||||||
|
else:
|
||||||
|
raise HTTPException(status_code=404, detail="File not found")
|
||||||
|
|
||||||
|
|
||||||
def get_current_username(
|
def get_current_username(
|
||||||
credentials: Annotated[HTTPBasicCredentials, Depends(security)]
|
credentials: Annotated[HTTPBasicCredentials, Depends(security)]
|
||||||
):
|
):
|
||||||
@ -334,7 +350,7 @@ def stop_all_runners():
|
|||||||
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Error: {res}:{id}")
|
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Error: {res}:{id}")
|
||||||
|
|
||||||
@app.get("/tradehistory/{symbol}", dependencies=[Depends(api_key_auth)])
|
@app.get("/tradehistory/{symbol}", dependencies=[Depends(api_key_auth)])
|
||||||
def get_trade_history(symbol: str, timestamp_from: float, timestamp_to:float) -> list[TradeView]:
|
def get_trade_history(symbol: str, timestamp_from: float, timestamp_to:float) -> list[Trade]:
|
||||||
res, set = cs.get_trade_history(symbol, timestamp_from, timestamp_to)
|
res, set = cs.get_trade_history(symbol, timestamp_from, timestamp_to)
|
||||||
if res == 0:
|
if res == 0:
|
||||||
return set
|
return set
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import numpy as np
|
|||||||
import v2realbot.controller.services as cs
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import AnalyzerInputs
|
from v2realbot.common.model import AnalyzerInputs
|
||||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
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
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import AnalyzerInputs
|
from v2realbot.common.model import AnalyzerInputs
|
||||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
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
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import AnalyzerInputs
|
from v2realbot.common.model import AnalyzerInputs
|
||||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
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
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import AnalyzerInputs
|
from v2realbot.common.model import AnalyzerInputs
|
||||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
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
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import AnalyzerInputs
|
from v2realbot.common.model import AnalyzerInputs
|
||||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
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
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import AnalyzerInputs
|
from v2realbot.common.model import AnalyzerInputs
|
||||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
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
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import AnalyzerInputs
|
from v2realbot.common.model import AnalyzerInputs
|
||||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
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
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import AnalyzerInputs
|
from v2realbot.common.model import AnalyzerInputs
|
||||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
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
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import AnalyzerInputs
|
from v2realbot.common.model import AnalyzerInputs
|
||||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||||
|
|||||||
@ -10,7 +10,7 @@ from enum import Enum
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import v2realbot.controller.services as cs
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||||
|
|||||||
@ -10,7 +10,7 @@ from enum import Enum
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import v2realbot.controller.services as cs
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
||||||
|
|||||||
@ -10,7 +10,7 @@ from enum import Enum
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import v2realbot.controller.services as cs
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
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
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import AnalyzerInputs
|
from v2realbot.common.model import AnalyzerInputs
|
||||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
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 scipy.stats import zscore
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from typing import Tuple, Optional, List
|
from typing import Tuple, Optional, List
|
||||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
|
|
||||||
def load_trades(runner_ids: List = None, batch_id: str = None) -> Tuple[int, List[Trade], int]:
|
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:
|
if runner_ids is None and batch_id is None:
|
||||||
|
|||||||
@ -10,7 +10,7 @@ from enum import Enum
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import v2realbot.controller.services as cs
|
import v2realbot.controller.services as cs
|
||||||
from rich import print
|
from rich import print
|
||||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
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.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.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.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.model import Trade, TradeDirection, TradeStatus, TradeStoplossType
|
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
|
||||||
from datetime import datetime
|
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
|
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
|
import numpy as np
|
||||||
|
|||||||
@ -11,44 +11,6 @@ var markersLine = null
|
|||||||
var avgBuyLine = null
|
var avgBuyLine = null
|
||||||
var profitLine = null
|
var profitLine = null
|
||||||
var slLine = []
|
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
|
//TRANSFORM object returned from REST API get_arch_run_detail
|
||||||
//to series and markers required by lightweigth chart
|
//to series and markers required by lightweigth chart
|
||||||
//input array object bars = { high: [1,2,3], time: [1,2,3], close: [2,2,2]...}
|
//input array object bars = { high: [1,2,3], time: [1,2,3], close: [2,2,2]...}
|
||||||
@ -72,11 +34,6 @@ function transform_data(data) {
|
|||||||
//cas of first record, nekdy jsou stejny - musim pridat setinku
|
//cas of first record, nekdy jsou stejny - musim pridat setinku
|
||||||
prev_cas = 0
|
prev_cas = 0
|
||||||
if ((data.ext_data !== null) && (data.ext_data.sl_history)) {
|
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) => {
|
data.ext_data.sl_history.forEach((histRecord, index, array) => {
|
||||||
|
|
||||||
//console.log("plnime")
|
//console.log("plnime")
|
||||||
@ -91,7 +48,6 @@ function transform_data(data) {
|
|||||||
//init nova sada
|
//init nova sada
|
||||||
sl_line_sada = []
|
sl_line_sada = []
|
||||||
sl_line_markers_sada = []
|
sl_line_markers_sada = []
|
||||||
sline_color = "#f5aa42"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
prev_id = histRecord.id
|
prev_id = histRecord.id
|
||||||
@ -109,21 +65,12 @@ function transform_data(data) {
|
|||||||
sline = {}
|
sline = {}
|
||||||
sline["time"] = cas
|
sline["time"] = cas
|
||||||
sline["value"] = histRecord.sl_val
|
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)
|
sl_line_sada.push(sline)
|
||||||
|
|
||||||
//ZDE JSEM SKONCIL
|
|
||||||
//COLOR SE NASTAVUJE V SERIES OPTIONS POZDEJI - nejak vymyslet
|
|
||||||
|
|
||||||
sline_markers = {}
|
sline_markers = {}
|
||||||
sline_markers["time"] = cas
|
sline_markers["time"] = cas
|
||||||
sline_markers["position"] = "inBar"
|
sline_markers["position"] = "inBar"
|
||||||
sline_markers["color"] = sline_color
|
sline_markers["color"] = "#f5aa42"
|
||||||
//sline_markers["shape"] = "circle"
|
//sline_markers["shape"] = "circle"
|
||||||
//console.log("SHOW_SL_DIGITS",SHOW_SL_DIGITS)
|
//console.log("SHOW_SL_DIGITS",SHOW_SL_DIGITS)
|
||||||
sline_markers["text"] = SHOW_SL_DIGITS ? histRecord.sl_val.toFixed(3) : ""
|
sline_markers["text"] = SHOW_SL_DIGITS ? histRecord.sl_val.toFixed(3) : ""
|
||||||
@ -292,33 +239,31 @@ function transform_data(data) {
|
|||||||
// //a_markers["text"] = CHART_SHOW_TEXT ? trade.position_qty + "/" + parseFloat(trade.pos_avg_price).toFixed(3) :trade.position_qty
|
// //a_markers["text"] = CHART_SHOW_TEXT ? trade.position_qty + "/" + parseFloat(trade.pos_avg_price).toFixed(3) :trade.position_qty
|
||||||
// avgp_markers.push(a_markers)
|
// avgp_markers.push(a_markers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { accid: accountId,buy: buyColor, sell: sellColor } = getAccountColors(trade.account);
|
|
||||||
|
|
||||||
//buy sell markery
|
//buy sell markery
|
||||||
marker = {}
|
marker = {}
|
||||||
marker["time"] = timestamp;
|
marker["time"] = timestamp;
|
||||||
// marker["position"] = (trade.order.side == "buy") ? "belowBar" : "aboveBar"
|
// marker["position"] = (trade.order.side == "buy") ? "belowBar" : "aboveBar"
|
||||||
marker["position"] = (trade.order.side == "buy") ? "aboveBar" : "aboveBar"
|
marker["position"] = (trade.order.side == "buy") ? "aboveBar" : "aboveBar"
|
||||||
marker["color"] = (trade.order.side == "buy") ? buyColor : sellColor
|
marker["color"] = (trade.order.side == "buy") ? "#37cade" : "red"
|
||||||
//marker["shape"] = (trade.order.side == "buy") ? "arrowUp" : "arrowDown"
|
//marker["shape"] = (trade.order.side == "buy") ? "arrowUp" : "arrowDown"
|
||||||
marker["shape"] = (trade.order.side == "buy") ? "arrowUp" : "arrowDown"
|
marker["shape"] = (trade.order.side == "buy") ? "arrowUp" : "arrowDown"
|
||||||
//marker["text"] = trade.qty + "/" + trade.price
|
//marker["text"] = trade.qty + "/" + trade.price
|
||||||
qt_optimized = (trade.order.qty % 1000 === 0) ? (trade.order.qty / 1000).toFixed(1) + 'K' : trade.order.qty
|
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) {
|
if (CHART_SHOW_TEXT) {
|
||||||
//včetně qty
|
//včetně qty
|
||||||
//marker["text"] = qt_optimized + "@" + trade.price
|
//marker["text"] = qt_optimized + "@" + trade.price
|
||||||
|
|
||||||
//bez qty
|
//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"
|
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 : ""
|
marker["text"] += (trade.position_qty == 0) ? closed_trade_marker_and_profit : ""
|
||||||
} else {
|
} else {
|
||||||
closed_trade_marker_and_profit = (trade.profit) ? "c" + trade.profit.toFixed(1) + "/" + trade.profit_sum.toFixed(1) : "c"
|
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)
|
markers.push(marker)
|
||||||
@ -899,7 +844,7 @@ function display_buy_markers(data) {
|
|||||||
//console.log("uvnitr")
|
//console.log("uvnitr")
|
||||||
slLine_temp = chart.addLineSeries({
|
slLine_temp = chart.addLineSeries({
|
||||||
// title: "avgpbuyline",
|
// title: "avgpbuyline",
|
||||||
color: slRecord[0]["color"] ? slRecord[0]["color"] : '#e4c76d',
|
color: '#e4c76d',
|
||||||
// color: 'transparent',
|
// color: 'transparent',
|
||||||
lineWidth: 1,
|
lineWidth: 1,
|
||||||
lastValueVisible: false
|
lastValueVisible: false
|
||||||
|
|||||||
@ -172,7 +172,7 @@ function initialize_archiveRecords() {
|
|||||||
{
|
{
|
||||||
targets: [13,14,15],
|
targets: [13,14,15],
|
||||||
render: function ( data, type, row ) {
|
render: function ( data, type, row ) {
|
||||||
return '<div class="tdsmall">'+JSON.stringify(data, null, 2)+'</div>'
|
return '<div class="tdsmall">'+data+'</div>'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -4,7 +4,7 @@ from v2realbot.utils.tlog import tlog, tlog_exception
|
|||||||
from v2realbot.enums.enums import Mode, Order, Account, RecordType, Followup
|
from v2realbot.enums.enums import Mode, Order, Account, RecordType, Followup
|
||||||
#from alpaca.trading.models import TradeUpdate
|
#from alpaca.trading.models import TradeUpdate
|
||||||
from v2realbot.common.model import TradeUpdate
|
from v2realbot.common.model import TradeUpdate
|
||||||
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
|
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
||||||
from alpaca.trading.enums import TradeEvent, OrderStatus
|
from alpaca.trading.enums import TradeEvent, OrderStatus
|
||||||
from v2realbot.indicators.indicators import ema
|
from v2realbot.indicators.indicators import ema
|
||||||
import orjson
|
import orjson
|
||||||
@ -44,8 +44,7 @@ class StrategyClassicSL(Strategy):
|
|||||||
msg = f"QUITTING {hard_cutoff=} MAX SUM REL PROFIT REACHED {max_sum_profit_to_quit_rel=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}"
|
msg = f"QUITTING {hard_cutoff=} MAX SUM REL PROFIT REACHED {max_sum_profit_to_quit_rel=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}"
|
||||||
printanyway(msg)
|
printanyway(msg)
|
||||||
self.state.ilog(e=msg)
|
self.state.ilog(e=msg)
|
||||||
for account in self.accounts:
|
self.state.vars.pending = "max_sum_profit_to_quit_rel"
|
||||||
self.state.account_variables[account.name].pending = "max_sum_profit_to_quit_rel"
|
|
||||||
if self.mode not in [Mode.BT, Mode.PREP]:
|
if self.mode not in [Mode.BT, Mode.PREP]:
|
||||||
send_to_telegram(msg)
|
send_to_telegram(msg)
|
||||||
if hard_cutoff:
|
if hard_cutoff:
|
||||||
@ -58,8 +57,7 @@ class StrategyClassicSL(Strategy):
|
|||||||
msg=f"QUITTING {hard_cutoff=} MAX SUM REL LOSS REACHED {max_sum_loss_to_quit_rel=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}"
|
msg=f"QUITTING {hard_cutoff=} MAX SUM REL LOSS REACHED {max_sum_loss_to_quit_rel=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}"
|
||||||
printanyway(msg)
|
printanyway(msg)
|
||||||
self.state.ilog(e=msg)
|
self.state.ilog(e=msg)
|
||||||
for account in self.accounts:
|
self.state.vars.pending = "max_sum_loss_to_quit_rel"
|
||||||
self.state.account_variables[account.name].pending = "max_sum_loss_to_quit_rel"
|
|
||||||
if self.mode not in [Mode.BT, Mode.PREP]:
|
if self.mode not in [Mode.BT, Mode.PREP]:
|
||||||
send_to_telegram(msg)
|
send_to_telegram(msg)
|
||||||
if hard_cutoff:
|
if hard_cutoff:
|
||||||
@ -73,8 +71,7 @@ class StrategyClassicSL(Strategy):
|
|||||||
msg = f"QUITTING {hard_cutoff=} MAX SUM ABS PROFIT REACHED {max_sum_profit_to_quit=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}"
|
msg = f"QUITTING {hard_cutoff=} MAX SUM ABS PROFIT REACHED {max_sum_profit_to_quit=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}"
|
||||||
printanyway(msg)
|
printanyway(msg)
|
||||||
self.state.ilog(e=msg)
|
self.state.ilog(e=msg)
|
||||||
for account in self.accounts:
|
self.state.vars.pending = "max_sum_profit_to_quit"
|
||||||
self.state.account_variables[account.name].pending = "max_sum_profit_to_quit"
|
|
||||||
if self.mode not in [Mode.BT, Mode.PREP]:
|
if self.mode not in [Mode.BT, Mode.PREP]:
|
||||||
send_to_telegram(msg)
|
send_to_telegram(msg)
|
||||||
if hard_cutoff:
|
if hard_cutoff:
|
||||||
@ -87,8 +84,7 @@ class StrategyClassicSL(Strategy):
|
|||||||
msg = f"QUITTING {hard_cutoff=} MAX SUM ABS LOSS REACHED {max_sum_loss_to_quit=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}"
|
msg = f"QUITTING {hard_cutoff=} MAX SUM ABS LOSS REACHED {max_sum_loss_to_quit=} {self.state.profit=} {rel_profit=} relprofits:{str(self.state.rel_profit_cum)}"
|
||||||
printanyway(msg)
|
printanyway(msg)
|
||||||
self.state.ilog(e=msg)
|
self.state.ilog(e=msg)
|
||||||
for account in self.accounts:
|
self.state.vars.pending = "max_sum_loss_to_quit"
|
||||||
self.state.account_variables[account.name].pending = "max_sum_loss_to_quit"
|
|
||||||
if self.mode not in [Mode.BT, Mode.PREP]:
|
if self.mode not in [Mode.BT, Mode.PREP]:
|
||||||
send_to_telegram(msg)
|
send_to_telegram(msg)
|
||||||
if hard_cutoff:
|
if hard_cutoff:
|
||||||
@ -99,10 +95,9 @@ class StrategyClassicSL(Strategy):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
async def add_followup(self, direction: TradeDirection, size: int, signal_name: str, account: Account):
|
async def add_followup(self, direction: TradeDirection, size: int, signal_name: str):
|
||||||
trade_to_add = Trade(
|
trade_to_add = Trade(
|
||||||
id=uuid4(),
|
id=uuid4(),
|
||||||
account=account,
|
|
||||||
last_update=datetime.fromtimestamp(self.state.time).astimezone(zoneNY),
|
last_update=datetime.fromtimestamp(self.state.time).astimezone(zoneNY),
|
||||||
status=TradeStatus.READY,
|
status=TradeStatus.READY,
|
||||||
size=size,
|
size=size,
|
||||||
@ -113,48 +108,45 @@ class StrategyClassicSL(Strategy):
|
|||||||
|
|
||||||
self.state.vars.prescribedTrades.append(trade_to_add)
|
self.state.vars.prescribedTrades.append(trade_to_add)
|
||||||
|
|
||||||
self.state.account_variables[account.name].requested_followup = None
|
self.state.vars.requested_followup = None
|
||||||
|
|
||||||
self.state.ilog(e=f"FOLLOWUP {direction} - {account} added to prescr.trades {signal_name=} {size=}", trade=trade_to_add)
|
self.state.ilog(e=f"FOLLOWUP {direction} added to prescr.trades {signal_name=} {size=}", trade=trade_to_add)
|
||||||
|
|
||||||
async def orderUpdateBuy(self, data: TradeUpdate):
|
async def orderUpdateBuy(self, data: TradeUpdate):
|
||||||
o: Order = data.order
|
o: Order = data.order
|
||||||
signal_name = None
|
signal_name = None
|
||||||
account = data.account
|
|
||||||
##nejak to vymyslet, aby se dal poslat cely Trade a serializoval se
|
##nejak to vymyslet, aby se dal poslat cely Trade a serializoval se
|
||||||
self.state.ilog(e="Příchozí BUY notif"+account, msg=o.status, trade=transform_data(data, json_serial))
|
self.state.ilog(e="Příchozí BUY notif", msg=o.status, trade=transform_data(data, json_serial))
|
||||||
|
|
||||||
if data.event == TradeEvent.FILL or data.event == TradeEvent.PARTIAL_FILL:
|
if data.event == TradeEvent.FILL or data.event == TradeEvent.PARTIAL_FILL:
|
||||||
|
|
||||||
#pokud jde o fill pred kterym je partail, muze se stat, ze uz budou vynulovany pozice, toto je pojistka
|
#pokud jde o fill pred kterym je partail, muze se stat, ze uz budou vynulovany pozice, toto je pojistka
|
||||||
#jde o uzavření short pozice - počítáme PROFIT
|
#jde o uzavření short pozice - počítáme PROFIT
|
||||||
if int(self.state.account_variables[account.name].positions) < 0 or (int(self.state.account_variables[account.name].positions) == 0 and self.state.account_variables[account.name].wait_for_fill is not None):
|
if int(self.state.positions) < 0 or (int(self.state.positions) == 0 and self.state.wait_for_fill is not None):
|
||||||
|
|
||||||
if data.event == TradeEvent.PARTIAL_FILL and self.state.account_variables[account.name].wait_for_fill is None:
|
if data.event == TradeEvent.PARTIAL_FILL and self.state.wait_for_fill is None:
|
||||||
#timto si oznacime, ze po partialu s vlivem na PROFIT musime cekat na FILL a zaroven ukladame prum cenu, kterou potrebujeme na vypocet profitu u fillu
|
#timto si oznacime, ze po partialu s vlivem na PROFIT musime cekat na FILL a zaroven ukladame prum cenu, kterou potrebujeme na vypocet profitu u fillu
|
||||||
self.state.account_variables[account.name].wait_for_fill = float(self.state.account_variables[account.name].avgp)
|
self.state.wait_for_fill = float(self.state.avgp)
|
||||||
|
|
||||||
#PROFIT pocitame z TradeUpdate.price a TradeUpdate.qty - aktualne provedene mnozstvi a cena
|
#PROFIT pocitame z TradeUpdate.price a TradeUpdate.qty - aktualne provedene mnozstvi a cena
|
||||||
#naklady vypocteme z prumerne ceny, kterou mame v pozicich
|
#naklady vypocteme z prumerne ceny, kterou mame v pozicich
|
||||||
bought_amount = data.qty * data.price
|
bought_amount = data.qty * data.price
|
||||||
#podle prumerne vstupni ceny, kolik stalo toto mnozstvi
|
#podle prumerne vstupni ceny, kolik stalo toto mnozstvi
|
||||||
if float(self.state.account_variables[account.name].avgp) > 0:
|
if float(self.state.avgp) > 0:
|
||||||
vstup_cena = float(self.state.account_variables[account.name].avgp)
|
vstup_cena = float(self.state.avgp)
|
||||||
elif float(self.state.account_variables[account.name].avgp) == 0 and self.state.account_variables[account.name].wait_for_fill is not None:
|
elif float(self.state.avgp) == 0 and self.state.wait_for_fill is not None:
|
||||||
vstup_cena = float(self.state.account_variables[account.name].wait_for_fill)
|
vstup_cena = float(self.state.wait_for_fill)
|
||||||
else:
|
else:
|
||||||
vstup_cena = 0
|
vstup_cena = 0
|
||||||
|
|
||||||
avg_costs = vstup_cena * float(data.qty)
|
avg_costs = vstup_cena * float(data.qty)
|
||||||
|
|
||||||
if avg_costs == 0:
|
if avg_costs == 0:
|
||||||
self.state.ilog(e="ERR: Nemame naklady na PROFIT, AVGP je nula. Zaznamenano jako 0"+account, msg="naklady=utrzena cena. TBD opravit.")
|
self.state.ilog(e="ERR: Nemame naklady na PROFIT, AVGP je nula. Zaznamenano jako 0", msg="naklady=utrzena cena. TBD opravit.")
|
||||||
avg_costs = bought_amount
|
avg_costs = bought_amount
|
||||||
|
|
||||||
trade_profit = round((avg_costs-bought_amount),2)
|
trade_profit = round((avg_costs-bought_amount),2)
|
||||||
#celkovy profit
|
self.state.profit += trade_profit
|
||||||
self.state.profit += trade_profit #overall abs profit
|
|
||||||
self.state.account_variables[account.name].profit += trade_profit #account profit
|
|
||||||
|
|
||||||
rel_profit = 0
|
rel_profit = 0
|
||||||
#spoctene celkovy relativni profit za trade v procentech ((trade_profit/vstup_naklady)*100)
|
#spoctene celkovy relativni profit za trade v procentech ((trade_profit/vstup_naklady)*100)
|
||||||
@ -168,32 +160,30 @@ class StrategyClassicSL(Strategy):
|
|||||||
if data.event == TradeEvent.FILL:
|
if data.event == TradeEvent.FILL:
|
||||||
#jde o partial EXIT dvááme si rel.profit do docasne promenne, po poslednim exitu z nich vypocteme skutecny rel.profit
|
#jde o partial EXIT dvááme si rel.profit do docasne promenne, po poslednim exitu z nich vypocteme skutecny rel.profit
|
||||||
if data.position_qty != 0:
|
if data.position_qty != 0:
|
||||||
self.state.account_variables[account.name].docasny_rel_profit.append(rel_profit)
|
self.state.docasny_rel_profit.append(rel_profit)
|
||||||
partial_exit = True
|
partial_exit = True
|
||||||
else:
|
else:
|
||||||
#jde o posledni z PARTIAL EXITU tzn.data.position_qty == 0
|
#jde o posledni z PARTIAL EXITU tzn.data.position_qty == 0
|
||||||
if len(self.state.account_variables[account.name].docasny_rel_profit) > 0:
|
if len(self.state.docasny_rel_profit) > 0:
|
||||||
#pricteme aktualni rel profit
|
#pricteme aktualni rel profit
|
||||||
self.state.account_variables[account.name].docasny_rel_profit.append(rel_profit)
|
self.state.docasny_rel_profit.append(rel_profit)
|
||||||
#a z rel profitu tohoto tradu vypocteme prumer, ktery teprve ulozime
|
#a z rel profitu tohoto tradu vypocteme prumer, ktery teprve ulozime
|
||||||
rel_profit = round(np.mean(self.state.account_variables[account.name].docasny_rel_profit),5)
|
rel_profit = round(np.mean(self.state.docasny_rel_profit),5)
|
||||||
self.state.account_variables[account.name].docasny_rel_profit = []
|
self.state.docasny_rel_profit = []
|
||||||
partial_last = True
|
partial_last = True
|
||||||
|
|
||||||
self.state.rel_profit_cum.append(rel_profit) #overall cum rel profit
|
self.state.rel_profit_cum.append(rel_profit)
|
||||||
self.state.account_variables[account.name].rel_profit_cum.append(rel_profit) #account cum rel profit
|
|
||||||
|
|
||||||
rel_profit_cum_calculated = round(np.sum(self.state.rel_profit_cum),5)
|
rel_profit_cum_calculated = round(np.sum(self.state.rel_profit_cum),5)
|
||||||
|
|
||||||
#pro martingale updatujeme loss_series_cnt -
|
#pro martingale updatujeme loss_series_cnt
|
||||||
self.state.vars["transferables"]["martingale"]["cont_loss_series_cnt"] = 0 if rel_profit > 0 else self.state.vars["transferables"]["martingale"]["cont_loss_series_cnt"]+1
|
self.state.vars["transferables"]["martingale"]["cont_loss_series_cnt"] = 0 if rel_profit > 0 else self.state.vars["transferables"]["martingale"]["cont_loss_series_cnt"]+1
|
||||||
self.state.ilog(lvl=1, e=f"update cont_loss_series_cnt na {self.state.vars['transferables']['martingale']['cont_loss_series_cnt']}")
|
self.state.ilog(lvl=1, e=f"update cont_loss_series_cnt na {self.state.vars['transferables']['martingale']['cont_loss_series_cnt']}")
|
||||||
|
|
||||||
self.state.ilog(e=f"BUY notif {account} - SHORT PROFIT: {partial_exit=} {partial_last=} {round(float(trade_profit),3)} celkem abs:{round(float(self.state.profit),3)} rel:{float(rel_profit)} rel_cum:{round(rel_profit_cum_calculated,7)}", msg=str(data.event), rel_profit_cum=str(self.state.rel_profit_cum), bought_amount=bought_amount, avg_costs=avg_costs, trade_qty=data.qty, trade_price=data.price, orderid=str(data.order.id))
|
self.state.ilog(e=f"BUY notif - SHORT PROFIT: {partial_exit=} {partial_last=} {round(float(trade_profit),3)} celkem:{round(float(self.state.profit),3)} rel:{float(rel_profit)} rel_cum:{round(rel_profit_cum_calculated,7)}", msg=str(data.event), rel_profit_cum=str(self.state.rel_profit_cum), bought_amount=bought_amount, avg_costs=avg_costs, trade_qty=data.qty, trade_price=data.price, orderid=str(data.order.id))
|
||||||
|
|
||||||
#zapsat profit do prescr.trades
|
#zapsat profit do prescr.trades
|
||||||
for trade in self.state.vars.prescribedTrades:
|
for trade in self.state.vars.prescribedTrades:
|
||||||
if trade.id == self.state.account_variables[account.name].pending:
|
if trade.id == self.state.vars.pending:
|
||||||
trade.last_update = datetime.fromtimestamp(self.state.time).astimezone(zoneNY)
|
trade.last_update = datetime.fromtimestamp(self.state.time).astimezone(zoneNY)
|
||||||
trade.profit += trade_profit
|
trade.profit += trade_profit
|
||||||
#pro ulozeni do tradeData scitame vsechen zisk z tohoto tradu (kvuli partialum)
|
#pro ulozeni do tradeData scitame vsechen zisk z tohoto tradu (kvuli partialum)
|
||||||
@ -211,7 +201,7 @@ class StrategyClassicSL(Strategy):
|
|||||||
|
|
||||||
if data.event == TradeEvent.FILL:
|
if data.event == TradeEvent.FILL:
|
||||||
#mazeme self.state.
|
#mazeme self.state.
|
||||||
self.state.account_variables[account.name].wait_for_fill = None
|
self.state.wait_for_fill = None
|
||||||
#zapsat update profitu do tradeList
|
#zapsat update profitu do tradeList
|
||||||
for tradeData in self.state.tradeList:
|
for tradeData in self.state.tradeList:
|
||||||
if tradeData.execution_id == data.execution_id:
|
if tradeData.execution_id == data.execution_id:
|
||||||
@ -219,7 +209,7 @@ class StrategyClassicSL(Strategy):
|
|||||||
setattr(tradeData, "profit", trade_profit)
|
setattr(tradeData, "profit", trade_profit)
|
||||||
setattr(tradeData, "profit_sum", self.state.profit)
|
setattr(tradeData, "profit_sum", self.state.profit)
|
||||||
setattr(tradeData, "signal_name", signal_name)
|
setattr(tradeData, "signal_name", signal_name)
|
||||||
setattr(tradeData, "prescribed_trade_id", self.state.account_variables[account.name].pending)
|
setattr(tradeData, "prescribed_trade_id", self.state.vars.pending)
|
||||||
#self.state.ilog(f"updatnut tradeList o profit", tradeData=orjson.loads(orjson.dumps(tradeData, default=json_serial, option=orjson.OPT_PASSTHROUGH_DATETIME)))
|
#self.state.ilog(f"updatnut tradeList o profit", tradeData=orjson.loads(orjson.dumps(tradeData, default=json_serial, option=orjson.OPT_PASSTHROUGH_DATETIME)))
|
||||||
setattr(tradeData, "rel_profit", rel_profit)
|
setattr(tradeData, "rel_profit", rel_profit)
|
||||||
setattr(tradeData, "rel_profit_cum", rel_profit_cum_calculated)
|
setattr(tradeData, "rel_profit_cum", rel_profit_cum_calculated)
|
||||||
@ -230,16 +220,16 @@ class StrategyClassicSL(Strategy):
|
|||||||
|
|
||||||
#pIF REVERSAL REQUIRED - reverse position is added to prescr.Trades with same signal name
|
#pIF REVERSAL REQUIRED - reverse position is added to prescr.Trades with same signal name
|
||||||
#jen při celém FILLU
|
#jen při celém FILLU
|
||||||
if data.event == TradeEvent.FILL and self.state.account_variables[account.name].requested_followup is not None:
|
if data.event == TradeEvent.FILL and self.state.vars.requested_followup is not None:
|
||||||
if self.state.account_variables[account.name].requested_followup == Followup.REVERSE:
|
if self.state.vars.requested_followup == Followup.REVERSE:
|
||||||
await self.add_followup(direction=TradeDirection.LONG, size=o.qty, signal_name=signal_name, account=account)
|
await self.add_followup(direction=TradeDirection.LONG, size=o.qty, signal_name=signal_name)
|
||||||
elif self.state.account_variables[account.name].requested_followup == Followup.ADD:
|
elif self.state.vars.requested_followup == Followup.ADD:
|
||||||
#zatim stejna SIZE
|
#zatim stejna SIZE
|
||||||
await self.add_followup(direction=TradeDirection.SHORT, size=o.qty, signal_name=signal_name, account=account)
|
await self.add_followup(direction=TradeDirection.SHORT, size=o.qty, signal_name=signal_name)
|
||||||
else:
|
else:
|
||||||
#zjistime nazev signalu a updatneme do tradeListu - abychom meli svazano
|
#zjistime nazev signalu a updatneme do tradeListu - abychom meli svazano
|
||||||
for trade in self.state.vars.prescribedTrades:
|
for trade in self.state.vars.prescribedTrades:
|
||||||
if trade.id == self.state.account_variables[account.name].pending:
|
if trade.id == self.state.vars.pending:
|
||||||
signal_name = trade.generated_by
|
signal_name = trade.generated_by
|
||||||
#zapiseme entry_time (jen pokud to neni partial add) - tzn. jen poprvé
|
#zapiseme entry_time (jen pokud to neni partial add) - tzn. jen poprvé
|
||||||
if data.event == TradeEvent.FILL and trade.entry_time is None:
|
if data.event == TradeEvent.FILL and trade.entry_time is None:
|
||||||
@ -249,7 +239,7 @@ class StrategyClassicSL(Strategy):
|
|||||||
for tradeData in self.state.tradeList:
|
for tradeData in self.state.tradeList:
|
||||||
if tradeData.execution_id == data.execution_id:
|
if tradeData.execution_id == data.execution_id:
|
||||||
setattr(tradeData, "signal_name", signal_name)
|
setattr(tradeData, "signal_name", signal_name)
|
||||||
setattr(tradeData, "prescribed_trade_id", self.state.account_variables[account.name].pending)
|
setattr(tradeData, "prescribed_trade_id", self.state.vars.pending)
|
||||||
|
|
||||||
self.state.ilog(e="BUY: Jde o LONG nakuú nepocitame profit zatim")
|
self.state.ilog(e="BUY: Jde o LONG nakuú nepocitame profit zatim")
|
||||||
|
|
||||||
@ -258,47 +248,43 @@ class StrategyClassicSL(Strategy):
|
|||||||
self.state.last_entry_price["long"] = data.price
|
self.state.last_entry_price["long"] = data.price
|
||||||
|
|
||||||
#pokud neni nastaveno goal_price tak vyplnujeme defaultem
|
#pokud neni nastaveno goal_price tak vyplnujeme defaultem
|
||||||
if self.state.account_variables[account.name].activeTrade.goal_price is None:
|
if self.state.vars.activeTrade.goal_price is None:
|
||||||
dat = dict(close=data.price)
|
dat = dict(close=data.price)
|
||||||
self.state.account_variables[account.name].activeTrade.goal_price = get_profit_target_price(self.state, dat, self.state.account_variables[account.name].activeTrade, TradeDirection.LONG)
|
self.state.vars.activeTrade.goal_price = get_profit_target_price(self.state, dat, TradeDirection.LONG)
|
||||||
|
|
||||||
#ic("vstupujeme do orderupdatebuy")
|
#ic("vstupujeme do orderupdatebuy")
|
||||||
print(data)
|
print(data)
|
||||||
#dostavame zde i celkové akutální množství - ukládáme
|
#dostavame zde i celkové akutální množství - ukládáme
|
||||||
self.state.account_variables[account.name].positions = data.position_qty
|
self.state.positions = data.position_qty
|
||||||
self.state.account_variables[account.name].avgp, self.state.account_variables[account.name].positions = self.state.interface[account.name].pos()
|
self.state.avgp, self.state.positions = self.state.interface.pos()
|
||||||
|
|
||||||
if o.status == OrderStatus.FILLED or o.status == OrderStatus.CANCELED:
|
if o.status == OrderStatus.FILLED or o.status == OrderStatus.CANCELED:
|
||||||
#davame pryc pending
|
#davame pryc pending
|
||||||
self.state.account_variables[account.name].pending = None
|
self.state.vars.pending = None
|
||||||
|
|
||||||
|
|
||||||
async def orderUpdateSell(self, data: TradeUpdate):
|
async def orderUpdateSell(self, data: TradeUpdate):
|
||||||
|
|
||||||
account = data.account
|
self.state.ilog(e="Příchozí SELL notif", msg=data.order.status, trade=transform_data(data, json_serial))
|
||||||
#TODO tady jsem skoncil, pak projit vsechen kod na state.avgp a prehodit
|
|
||||||
|
|
||||||
|
|
||||||
self.state.ilog(e=f"Příchozí SELL notif - {account}", msg=data.order.status, trade=transform_data(data, json_serial))
|
|
||||||
|
|
||||||
#naklady vypocteme z prumerne ceny, kterou mame v pozicich
|
#naklady vypocteme z prumerne ceny, kterou mame v pozicich
|
||||||
if data.event == TradeEvent.FILL or data.event == TradeEvent.PARTIAL_FILL:
|
if data.event == TradeEvent.FILL or data.event == TradeEvent.PARTIAL_FILL:
|
||||||
|
|
||||||
#pokud jde o fill pred kterym je partail, muze se stat, ze uz budou vynulovany pozice, toto je pojistka
|
#pokud jde o fill pred kterym je partail, muze se stat, ze uz budou vynulovany pozice, toto je pojistka
|
||||||
#jde o uzavření long pozice - počítáme PROFIT
|
#jde o uzavření long pozice - počítáme PROFIT
|
||||||
if int(self.state.account_variables[account.name].positions) > 0 or (int(self.state.account_variables[account.name].positions) == 0 and self.state.account_variables[account.name].wait_for_fill is not None):
|
if int(self.state.positions) > 0 or (int(self.state.positions) == 0 and self.state.wait_for_fill is not None):
|
||||||
|
|
||||||
if data.event == TradeEvent.PARTIAL_FILL and self.state.account_variables[account.name].wait_for_fill is None:
|
if data.event == TradeEvent.PARTIAL_FILL and self.state.wait_for_fill is None:
|
||||||
#timto si oznacime, ze po partialu s vlivem na PROFIT musime cekat na FILL a zaroven ukladame prum cenu, kterou potrebujeme na vypocet profitu u fillu
|
#timto si oznacime, ze po partialu s vlivem na PROFIT musime cekat na FILL a zaroven ukladame prum cenu, kterou potrebujeme na vypocet profitu u fillu
|
||||||
self.state.account_variables[account.name].wait_for_fill = float(self.state.account_variables[account.name].avgp)
|
self.state.wait_for_fill = float(self.state.avgp)
|
||||||
|
|
||||||
#PROFIT pocitame z TradeUpdate.price a TradeUpdate.qty - aktualne provedene mnozstvi a cena
|
#PROFIT pocitame z TradeUpdate.price a TradeUpdate.qty - aktualne provedene mnozstvi a cena
|
||||||
#naklady vypocteme z prumerne ceny, kterou mame v pozicich
|
#naklady vypocteme z prumerne ceny, kterou mame v pozicich
|
||||||
sold_amount = data.qty * data.price
|
sold_amount = data.qty * data.price
|
||||||
if float(self.state.account_variables[account.name].avgp) > 0:
|
if float(self.state.avgp) > 0:
|
||||||
vstup_cena = float(self.state.account_variables[account.name].avgp)
|
vstup_cena = float(self.state.avgp)
|
||||||
elif float(self.state.account_variables[account.name].avgp) == 0 and self.state.account_variables[account.name].wait_for_fill is not None:
|
elif float(self.state.avgp) == 0 and self.state.wait_for_fill is not None:
|
||||||
vstup_cena = float(self.state.account_variables[account.name].wait_for_fill)
|
vstup_cena = float(self.state.wait_for_fill)
|
||||||
else:
|
else:
|
||||||
vstup_cena = 0
|
vstup_cena = 0
|
||||||
|
|
||||||
@ -306,13 +292,12 @@ class StrategyClassicSL(Strategy):
|
|||||||
avg_costs = vstup_cena * float(data.qty)
|
avg_costs = vstup_cena * float(data.qty)
|
||||||
|
|
||||||
if avg_costs == 0:
|
if avg_costs == 0:
|
||||||
self.state.ilog(e=f"ERR: {account} Nemame naklady na PROFIT, AVGP je nula. Zaznamenano jako 0", msg="naklady=utrzena cena. TBD opravit.")
|
self.state.ilog(e="ERR: Nemame naklady na PROFIT, AVGP je nula. Zaznamenano jako 0", msg="naklady=utrzena cena. TBD opravit.")
|
||||||
avg_costs = sold_amount
|
avg_costs = sold_amount
|
||||||
|
|
||||||
trade_profit = round((sold_amount - avg_costs),2)
|
trade_profit = round((sold_amount - avg_costs),2)
|
||||||
self.state.profit += trade_profit #celkový profit
|
self.state.profit += trade_profit
|
||||||
self.state.account_variables[account.name].profit += trade_profit #account specific profit
|
|
||||||
|
|
||||||
rel_profit = 0
|
rel_profit = 0
|
||||||
#spoctene celkovy relativni profit za trade v procentech ((trade_profit/vstup_naklady)*100)
|
#spoctene celkovy relativni profit za trade v procentech ((trade_profit/vstup_naklady)*100)
|
||||||
if vstup_cena != 0 and data.order.qty != 0:
|
if vstup_cena != 0 and data.order.qty != 0:
|
||||||
@ -324,31 +309,30 @@ class StrategyClassicSL(Strategy):
|
|||||||
if data.event == TradeEvent.FILL:
|
if data.event == TradeEvent.FILL:
|
||||||
#jde o partial EXIT dvááme si rel.profit do docasne promenne, po poslednim exitu z nich vypocteme skutecny rel.profit
|
#jde o partial EXIT dvááme si rel.profit do docasne promenne, po poslednim exitu z nich vypocteme skutecny rel.profit
|
||||||
if data.position_qty != 0:
|
if data.position_qty != 0:
|
||||||
self.state.account_variables[account.name].docasny_rel_profit.append(rel_profit)
|
self.state.docasny_rel_profit.append(rel_profit)
|
||||||
partial_exit = True
|
partial_exit = True
|
||||||
else:
|
else:
|
||||||
#jde o posledni z PARTIAL EXITU tzn.data.position_qty == 0
|
#jde o posledni z PARTIAL EXITU tzn.data.position_qty == 0
|
||||||
if len(self.state.account_variables[account.name].docasny_rel_profit) > 0:
|
if len(self.state.docasny_rel_profit) > 0:
|
||||||
#pricteme aktualni rel profit
|
#pricteme aktualni rel profit
|
||||||
self.state.account_variables[account.name].docasny_rel_profit.append(rel_profit)
|
self.state.docasny_rel_profit.append(rel_profit)
|
||||||
#a z rel profitu tohoto tradu vypocteme prumer, ktery teprve ulozime
|
#a z rel profitu tohoto tradu vypocteme prumer, ktery teprve ulozime
|
||||||
rel_profit = round(np.mean(self.state.account_variables[account.name].docasny_rel_profit),5)
|
rel_profit = round(np.mean(self.state.docasny_rel_profit),5)
|
||||||
self.state.account_variables[account.name].docasny_rel_profit = []
|
self.state.docasny_rel_profit = []
|
||||||
partial_last = True
|
partial_last = True
|
||||||
|
|
||||||
self.state.rel_profit_cum.append(rel_profit) #overall rel profit
|
self.state.rel_profit_cum.append(rel_profit)
|
||||||
self.state.account_variables[account.name].rel_profit_cum.append(rel_profit) #account cum rel profit
|
|
||||||
rel_profit_cum_calculated = round(np.sum(self.state.rel_profit_cum),5)
|
rel_profit_cum_calculated = round(np.sum(self.state.rel_profit_cum),5)
|
||||||
|
|
||||||
#pro martingale updatujeme loss_series_cnt
|
#pro martingale updatujeme loss_series_cnt
|
||||||
self.state.vars["transferables"]["martingale"]["cont_loss_series_cnt"] = 0 if rel_profit > 0 else self.state.vars["transferables"]["martingale"]["cont_loss_series_cnt"]+1
|
self.state.vars["transferables"]["martingale"]["cont_loss_series_cnt"] = 0 if rel_profit > 0 else self.state.vars["transferables"]["martingale"]["cont_loss_series_cnt"]+1
|
||||||
self.state.ilog(lvl=1, e=f"update cont_loss_series_cnt na {self.state.vars['transferables']['martingale']['cont_loss_series_cnt']}")
|
self.state.ilog(lvl=1, e=f"update cont_loss_series_cnt na {self.state.vars['transferables']['martingale']['cont_loss_series_cnt']}")
|
||||||
|
|
||||||
self.state.ilog(e=f"SELL notif {account.name}- LONG PROFIT {partial_exit=} {partial_last=}:{round(float(trade_profit),3)} celkem:{round(float(self.state.profit),3)} rel:{float(rel_profit)} rel_cum:{round(rel_profit_cum_calculated,7)}", msg=str(data.event), rel_profit_cum = str(self.state.rel_profit_cum), sold_amount=sold_amount, avg_costs=avg_costs, trade_qty=data.qty, trade_price=data.price, orderid=str(data.order.id))
|
self.state.ilog(e=f"SELL notif - LONG PROFIT {partial_exit=} {partial_last=}:{round(float(trade_profit),3)} celkem:{round(float(self.state.profit),3)} rel:{float(rel_profit)} rel_cum:{round(rel_profit_cum_calculated,7)}", msg=str(data.event), rel_profit_cum = str(self.state.rel_profit_cum), sold_amount=sold_amount, avg_costs=avg_costs, trade_qty=data.qty, trade_price=data.price, orderid=str(data.order.id))
|
||||||
|
|
||||||
#zapsat profit do prescr.trades
|
#zapsat profit do prescr.trades
|
||||||
for trade in self.state.vars.prescribedTrades:
|
for trade in self.state.vars.prescribedTrades:
|
||||||
if trade.id == self.state.account_variables[account.name].pending:
|
if trade.id == self.state.vars.pending:
|
||||||
trade.last_update = datetime.fromtimestamp(self.state.time).astimezone(zoneNY)
|
trade.last_update = datetime.fromtimestamp(self.state.time).astimezone(zoneNY)
|
||||||
trade.profit += trade_profit
|
trade.profit += trade_profit
|
||||||
#pro ulozeni do tradeData scitame vsechen zisk z tohoto tradu (kvuli partialum)
|
#pro ulozeni do tradeData scitame vsechen zisk z tohoto tradu (kvuli partialum)
|
||||||
@ -365,7 +349,7 @@ class StrategyClassicSL(Strategy):
|
|||||||
|
|
||||||
if data.event == TradeEvent.FILL:
|
if data.event == TradeEvent.FILL:
|
||||||
#mazeme self.state.
|
#mazeme self.state.
|
||||||
self.state.account_variables[account.name].wait_for_fill = None
|
self.state.wait_for_fill = None
|
||||||
#zapsat update profitu do tradeList
|
#zapsat update profitu do tradeList
|
||||||
for tradeData in self.state.tradeList:
|
for tradeData in self.state.tradeList:
|
||||||
if tradeData.execution_id == data.execution_id:
|
if tradeData.execution_id == data.execution_id:
|
||||||
@ -373,7 +357,7 @@ class StrategyClassicSL(Strategy):
|
|||||||
setattr(tradeData, "profit", trade_profit)
|
setattr(tradeData, "profit", trade_profit)
|
||||||
setattr(tradeData, "profit_sum", self.state.profit)
|
setattr(tradeData, "profit_sum", self.state.profit)
|
||||||
setattr(tradeData, "signal_name", signal_name)
|
setattr(tradeData, "signal_name", signal_name)
|
||||||
setattr(tradeData, "prescribed_trade_id", self.state.account_variables[account.name].pending)
|
setattr(tradeData, "prescribed_trade_id", self.state.vars.pending)
|
||||||
#self.state.ilog(f"updatnut tradeList o profi {str(tradeData)}")
|
#self.state.ilog(f"updatnut tradeList o profi {str(tradeData)}")
|
||||||
setattr(tradeData, "rel_profit", rel_profit)
|
setattr(tradeData, "rel_profit", rel_profit)
|
||||||
setattr(tradeData, "rel_profit_cum", rel_profit_cum_calculated)
|
setattr(tradeData, "rel_profit_cum", rel_profit_cum_calculated)
|
||||||
@ -383,17 +367,17 @@ class StrategyClassicSL(Strategy):
|
|||||||
if data.event == TradeEvent.FILL and await self.stop_when_max_profit_loss() is False:
|
if data.event == TradeEvent.FILL and await self.stop_when_max_profit_loss() is False:
|
||||||
|
|
||||||
#IF REVERSAL REQUIRED - reverse position is added to prescr.Trades with same signal name
|
#IF REVERSAL REQUIRED - reverse position is added to prescr.Trades with same signal name
|
||||||
if data.event == TradeEvent.FILL and self.state.account_variables[account.name].requested_followup is not None:
|
if data.event == TradeEvent.FILL and self.state.vars.requested_followup is not None:
|
||||||
if self.state.account_variables[account.name].requested_followup == Followup.REVERSE:
|
if self.state.vars.requested_followup == Followup.REVERSE:
|
||||||
await self.add_followup(direction=TradeDirection.SHORT, size=data.order.qty, signal_name=signal_name)
|
await self.add_followup(direction=TradeDirection.SHORT, size=data.order.qty, signal_name=signal_name)
|
||||||
elif self.state.account_variables[account.name].requested_followup == Followup.ADD:
|
elif self.state.vars.requested_followup == Followup.ADD:
|
||||||
#zatim stejna SIZE
|
#zatim stejna SIZE
|
||||||
await self.add_followup(direction=TradeDirection.LONG, size=data.order.qty, signal_name=signal_name)
|
await self.add_followup(direction=TradeDirection.LONG, size=data.order.qty, signal_name=signal_name)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
#zjistime nazev signalu a updatneme do tradeListu - abychom meli svazano
|
#zjistime nazev signalu a updatneme do tradeListu - abychom meli svazano
|
||||||
for trade in self.state.vars.prescribedTrades:
|
for trade in self.state.vars.prescribedTrades:
|
||||||
if trade.id == self.state.account_variables[account.name].pending:
|
if trade.id == self.state.vars.pending:
|
||||||
signal_name = trade.generated_by
|
signal_name = trade.generated_by
|
||||||
#zapiseme entry_time (jen pokud to neni partial add) - tzn. jen poprvé
|
#zapiseme entry_time (jen pokud to neni partial add) - tzn. jen poprvé
|
||||||
if data.event == TradeEvent.FILL and trade.entry_time is None:
|
if data.event == TradeEvent.FILL and trade.entry_time is None:
|
||||||
@ -403,7 +387,7 @@ class StrategyClassicSL(Strategy):
|
|||||||
for tradeData in self.state.tradeList:
|
for tradeData in self.state.tradeList:
|
||||||
if tradeData.execution_id == data.execution_id:
|
if tradeData.execution_id == data.execution_id:
|
||||||
setattr(tradeData, "signal_name", signal_name)
|
setattr(tradeData, "signal_name", signal_name)
|
||||||
setattr(tradeData, "prescribed_trade_id", self.state.account_variables[account.name].pending)
|
setattr(tradeData, "prescribed_trade_id", self.state.vars.pending)
|
||||||
|
|
||||||
self.state.ilog(e="SELL: Jde o SHORT nepocitame profit zatim")
|
self.state.ilog(e="SELL: Jde o SHORT nepocitame profit zatim")
|
||||||
|
|
||||||
@ -411,32 +395,32 @@ class StrategyClassicSL(Strategy):
|
|||||||
#zapisujeme last entry price
|
#zapisujeme last entry price
|
||||||
self.state.last_entry_price["short"] = data.price
|
self.state.last_entry_price["short"] = data.price
|
||||||
#pokud neni nastaveno goal_price tak vyplnujeme defaultem
|
#pokud neni nastaveno goal_price tak vyplnujeme defaultem
|
||||||
if self.state.account_variables[account.name].activeTrade.goal_price is None:
|
if self.state.vars.activeTrade.goal_price is None:
|
||||||
dat = dict(close=data.price)
|
dat = dict(close=data.price)
|
||||||
self.state.account_variables[account.name].activeTrade.goal_price = get_profit_target_price(self.state, dat, self.state.account_variables[account.name].activeTrade, TradeDirection.SHORT)
|
self.state.vars.activeTrade.goal_price = get_profit_target_price(self.state, dat, TradeDirection.SHORT)
|
||||||
#sem v budoucnu dat i update SL
|
#sem v budoucnu dat i update SL
|
||||||
#if self.state.vars.activeTrade.stoploss_value is None:
|
#if self.state.vars.activeTrade.stoploss_value is None:
|
||||||
|
|
||||||
|
|
||||||
#update pozic, v trade update je i pocet zbylych pozic
|
#update pozic, v trade update je i pocet zbylych pozic
|
||||||
old_avgp = self.state.account_variables[account.name].avgp
|
old_avgp = self.state.avgp
|
||||||
old_pos = self.state.account_variables[account.name].positions
|
old_pos = self.state.positions
|
||||||
self.state.account_variables[account.name].positions = int(data.position_qty)
|
self.state.positions = int(data.position_qty)
|
||||||
if int(data.position_qty) == 0:
|
if int(data.position_qty) == 0:
|
||||||
self.state.account_variables[account.name].avgp = 0
|
self.state.avgp = 0
|
||||||
|
|
||||||
self.state.ilog(e="SELL notifikace "+str(data.order.status), msg="update pozic", old_avgp=old_avgp, old_pos=old_pos, avgp=self.state.account_variables[account.name].avgp, pos=self.state.account_variables[account.name].positions, orderid=str(data.order.id))
|
self.state.ilog(e="SELL notifikace "+str(data.order.status), msg="update pozic", old_avgp=old_avgp, old_pos=old_pos, avgp=self.state.avgp, pos=self.state.positions, orderid=str(data.order.id))
|
||||||
#self.state.account_variables[account.name].avgp, self.state.account_variables[account.name].positions = self.interface.pos()
|
#self.state.avgp, self.state.positions = self.interface.pos()
|
||||||
|
|
||||||
if data.event == TradeEvent.FILL or data.event == TradeEvent.CANCELED:
|
if data.event == TradeEvent.FILL or data.event == TradeEvent.CANCELED:
|
||||||
print("Příchozí SELL notifikace - complete FILL nebo CANCEL", data.event)
|
print("Příchozí SELL notifikace - complete FILL nebo CANCEL", data.event)
|
||||||
self.state.account_variables[account.name].pending = None
|
self.state.vars.pending = None
|
||||||
a,p = self.interface[account.name].pos() #TBD maybe optimize for speed
|
a,p = self.interface.pos()
|
||||||
#pri chybe api nechavame puvodni hodnoty
|
#pri chybe api nechavame puvodni hodnoty
|
||||||
if a != -1:
|
if a != -1:
|
||||||
self.state.account_variables[account.name].avgp, self.state.account_variables[account.name].positions = a,p
|
self.state.avgp, self.state.positions = a,p
|
||||||
else: self.state.ilog(e=f"Chyba pri dotažení self.interface.pos() {a}")
|
else: self.state.ilog(e=f"Chyba pri dotažení self.interface.pos() {a}")
|
||||||
#ic(self.state.account_variables[account.name].avgp, self.state.account_variables[account.name].positions)
|
#ic(self.state.avgp, self.state.positions)
|
||||||
|
|
||||||
#this parent method is called by strategy just once before waiting for first data
|
#this parent method is called by strategy just once before waiting for first data
|
||||||
def strat_init(self):
|
def strat_init(self):
|
||||||
@ -457,47 +441,48 @@ class StrategyClassicSL(Strategy):
|
|||||||
else:
|
else:
|
||||||
self.next(item, self.state)
|
self.next(item, self.state)
|
||||||
|
|
||||||
|
|
||||||
#overidden methods
|
#overidden methods
|
||||||
# pouziva se pri vstupu long nebo exitu short
|
# pouziva se pri vstupu long nebo exitu short
|
||||||
# osetrit uzavreni s vice nez mam
|
# osetrit uzavreni s vice nez mam
|
||||||
def buy(self, account: Account, size = None, repeat: bool = False):
|
def buy(self, size = None, repeat: bool = False):
|
||||||
print("overriden buy method")
|
print("overriden buy method")
|
||||||
if size is None:
|
if size is None:
|
||||||
sizer = self.state.vars.chunk
|
sizer = self.state.vars.chunk
|
||||||
else:
|
else:
|
||||||
sizer = size
|
sizer = size
|
||||||
#jde o uzavreni short pozice
|
#jde o uzavreni short pozice
|
||||||
if int(self.state.account_variables[account.name].positions) < 0 and (int(self.state.account_variables[account.name].positions) + int(sizer)) > 0:
|
if int(self.state.positions) < 0 and (int(self.state.positions) + int(sizer)) > 0:
|
||||||
self.state.ilog(e="buy nelze nakoupit vic nez shortuji", positions=self.state.account_variables[account.name].positions, size=size)
|
self.state.ilog(e="buy nelze nakoupit vic nez shortuji", positions=self.state.positions, size=size)
|
||||||
printanyway("buy nelze nakoupit vic nez shortuji")
|
printanyway("buy nelze nakoupit vic nez shortuji")
|
||||||
return -2
|
return -2
|
||||||
|
|
||||||
if int(self.state.account_variables[account.name].positions) >= self.state.vars.maxpozic:
|
if int(self.state.positions) >= self.state.vars.maxpozic:
|
||||||
self.state.ilog(e="buy Maxim mnozstvi naplneno", positions=self.state.account_variables[account.name].positions)
|
self.state.ilog(e="buy Maxim mnozstvi naplneno", positions=self.state.positions)
|
||||||
printanyway("max mnostvi naplneno")
|
printanyway("max mnostvi naplneno")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
self.state.blockbuy = 1
|
||||||
|
self.state.vars.lastbuyindex = self.state.bars['index'][-1]
|
||||||
#self.state.ilog(e="send MARKET buy to if", msg="S:"+str(size), ltp=self.state.interface.get_last_price(self.state.symbol))
|
#self.state.ilog(e="send MARKET buy to if", msg="S:"+str(size), ltp=self.state.interface.get_last_price(self.state.symbol))
|
||||||
self.state.ilog(e="send MARKET buy to if", msg="S:"+str(size), ltp=self.state.bars['close'][-1])
|
self.state.ilog(e="send MARKET buy to if", msg="S:"+str(size), ltp=self.state.bars['close'][-1])
|
||||||
return self.state.interface[account.name].buy(size=sizer)
|
return self.state.interface.buy(size=sizer)
|
||||||
|
|
||||||
#overidden methods
|
#overidden methods
|
||||||
# pouziva se pri vstupu short nebo exitu long
|
# pouziva se pri vstupu short nebo exitu long
|
||||||
def sell(self, account: Account, size = None, repeat: bool = False):
|
def sell(self, size = None, repeat: bool = False):
|
||||||
print("overriden sell method")
|
print("overriden sell method")
|
||||||
if size is None:
|
if size is None:
|
||||||
size = abs(int(self.state.account_variables[account.name].positions))
|
size = abs(int(self.state.positions))
|
||||||
|
|
||||||
#jde o uzavreni long pozice
|
#jde o uzavreni long pozice
|
||||||
if int(self.state.account_variables[account.name].positions) > 0 and (int(self.state.account_variables[account.name].positions) - int(size)) < 0:
|
if int(self.state.positions) > 0 and (int(self.state.positions) - int(size)) < 0:
|
||||||
self.state.ilog(e="nelze prodat vic nez longuji", positions=self.state.account_variables[account.name].positions, size=size)
|
self.state.ilog(e="nelze prodat vic nez longuji", positions=self.state.positions, size=size)
|
||||||
printanyway("nelze prodat vic nez longuji")
|
printanyway("nelze prodat vic nez longuji")
|
||||||
return -2
|
return -2
|
||||||
|
|
||||||
#pokud shortuji a mam max pozic
|
#pokud shortuji a mam max pozic
|
||||||
if int(self.state.account_variables[account.name].positions) < 0 and abs(int(self.state.account_variables[account.name].positions)) >= self.state.vars.maxpozic:
|
if int(self.state.positions) < 0 and abs(int(self.state.positions)) >= self.state.vars.maxpozic:
|
||||||
self.state.ilog(e="short - Maxim mnozstvi naplneno", positions=self.state.account_variables[account.name].positions, size=size)
|
self.state.ilog(e="short - Maxim mnozstvi naplneno", positions=self.state.positions, size=size)
|
||||||
printanyway("short - Maxim mnozstvi naplneno")
|
printanyway("short - Maxim mnozstvi naplneno")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@ -505,4 +490,4 @@ class StrategyClassicSL(Strategy):
|
|||||||
#self.state.vars.lastbuyindex = self.state.bars['index'][-1]
|
#self.state.vars.lastbuyindex = self.state.bars['index'][-1]
|
||||||
#self.state.ilog(e="send MARKET SELL to if", msg="S:"+str(size), ltp=self.state.interface.get_last_price(self.state.symbol))
|
#self.state.ilog(e="send MARKET SELL to if", msg="S:"+str(size), ltp=self.state.interface.get_last_price(self.state.symbol))
|
||||||
self.state.ilog(e="send MARKET SELL to if", msg="S:"+str(size), ltp=self.state.bars['close'][-1])
|
self.state.ilog(e="send MARKET SELL to if", msg="S:"+str(size), ltp=self.state.bars['close'][-1])
|
||||||
return self.state.interface[account.name].sell(size=size)
|
return self.state.interface.sell(size=size)
|
||||||
@ -2,7 +2,7 @@
|
|||||||
Strategy base class
|
Strategy base class
|
||||||
"""
|
"""
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from v2realbot.utils.utils import AttributeDict, zoneNY, is_open_rush, is_close_rush, json_serial, print, gaka
|
from v2realbot.utils.utils import AttributeDict, zoneNY, is_open_rush, is_close_rush, json_serial, print
|
||||||
from v2realbot.utils.tlog import tlog
|
from v2realbot.utils.tlog import tlog
|
||||||
from v2realbot.utils.ilog import insert_log, insert_log_multiple_queue
|
from v2realbot.utils.ilog import insert_log, insert_log_multiple_queue
|
||||||
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Order, Account
|
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Order, Account
|
||||||
@ -16,11 +16,11 @@ from v2realbot.loader.trade_ws_streamer import Trade_WS_Streamer
|
|||||||
from v2realbot.interfaces.general_interface import GeneralInterface
|
from v2realbot.interfaces.general_interface import GeneralInterface
|
||||||
from v2realbot.interfaces.backtest_interface import BacktestInterface
|
from v2realbot.interfaces.backtest_interface import BacktestInterface
|
||||||
from v2realbot.interfaces.live_interface import LiveInterface
|
from v2realbot.interfaces.live_interface import LiveInterface
|
||||||
import v2realbot.common.model as ptm
|
import v2realbot.common.PrescribedTradeModel as ptm
|
||||||
from alpaca.trading.enums import OrderSide
|
from alpaca.trading.enums import OrderSide
|
||||||
from v2realbot.backtesting.backtester import Backtester
|
from v2realbot.backtesting.backtester import Backtester
|
||||||
#from alpaca.trading.models import TradeUpdate
|
#from alpaca.trading.models import TradeUpdate
|
||||||
from v2realbot.common.model import TradeUpdate, AccountVariables
|
from v2realbot.common.model import TradeUpdate
|
||||||
from alpaca.trading.enums import TradeEvent, OrderStatus
|
from alpaca.trading.enums import TradeEvent, OrderStatus
|
||||||
from threading import Event, current_thread
|
from threading import Event, current_thread
|
||||||
import orjson
|
import orjson
|
||||||
@ -30,7 +30,6 @@ from collections import defaultdict
|
|||||||
import v2realbot.strategyblocks.activetrade.sl.optimsl as optimsl
|
import v2realbot.strategyblocks.activetrade.sl.optimsl as optimsl
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
import v2realbot.utils.config_handler as cfh
|
import v2realbot.utils.config_handler as cfh
|
||||||
from typing import Dict, Set
|
|
||||||
|
|
||||||
if PROFILING_NEXT_ENABLED:
|
if PROFILING_NEXT_ENABLED:
|
||||||
from pyinstrument import Profiler
|
from pyinstrument import Profiler
|
||||||
@ -51,8 +50,7 @@ class Strategy:
|
|||||||
self.rectype: RecordType = None
|
self.rectype: RecordType = None
|
||||||
self.nextnew = 1
|
self.nextnew = 1
|
||||||
self.btdata: list = []
|
self.btdata: list = []
|
||||||
self.interface: Dict[str, GeneralInterface] = {}
|
self.interface: GeneralInterface = None
|
||||||
self.order_notifs: Dict[str, LiveOrderUpdatesStreamer] = {}
|
|
||||||
self.state: StrategyState = None
|
self.state: StrategyState = None
|
||||||
self.bt: Backtester = None
|
self.bt: Backtester = None
|
||||||
self.debug = False
|
self.debug = False
|
||||||
@ -62,8 +60,8 @@ class Strategy:
|
|||||||
self.open_rush = open_rush
|
self.open_rush = open_rush
|
||||||
self.close_rush = close_rush
|
self.close_rush = close_rush
|
||||||
self._streams = []
|
self._streams = []
|
||||||
#primary account from runReqs
|
|
||||||
self.account = account
|
self.account = account
|
||||||
|
self.key = get_key(mode=self.mode, account=self.account)
|
||||||
self.rtqueue = None
|
self.rtqueue = None
|
||||||
self.runner_id = runner_id
|
self.runner_id = runner_id
|
||||||
self.ilog_save = ilog_save
|
self.ilog_save = ilog_save
|
||||||
@ -71,9 +69,6 @@ class Strategy:
|
|||||||
self.secondary_res_start_index = dict()
|
self.secondary_res_start_index = dict()
|
||||||
self.last_index = -1
|
self.last_index = -1
|
||||||
|
|
||||||
#set of all accounts (Account) including those from stratvars
|
|
||||||
self.accounts = self.get_accounts_in_stratvars_and_reqs()
|
|
||||||
|
|
||||||
#TODO predelat na dynamické queues
|
#TODO predelat na dynamické queues
|
||||||
self.q1 = queue.Queue()
|
self.q1 = queue.Queue()
|
||||||
self.q2 = queue.Queue()
|
self.q2 = queue.Queue()
|
||||||
@ -88,25 +83,6 @@ class Strategy:
|
|||||||
self.hard_stop = False #indikuje hard stop, tedy vypnuti strategie
|
self.hard_stop = False #indikuje hard stop, tedy vypnuti strategie
|
||||||
self.soft_stop = False #indikuje soft stop (napr. při dosažení max zisku/ztráty), tedy pokracovani strategie, vytvareni dat, jen bez obchodu
|
self.soft_stop = False #indikuje soft stop (napr. při dosažení max zisku/ztráty), tedy pokracovani strategie, vytvareni dat, jen bez obchodu
|
||||||
|
|
||||||
def get_accounts_in_stratvars_and_reqs(self) -> Set:
|
|
||||||
"""
|
|
||||||
Helper that retrieves distinct account values used in stratvars and in runRequest.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
set: A set of unique account values.
|
|
||||||
"""
|
|
||||||
account_keywords = ['account', 'account_long', 'account_short']
|
|
||||||
account_values = set()
|
|
||||||
|
|
||||||
for signal_value in self.stratvars.get('signals', {}).values():
|
|
||||||
for key in account_keywords:
|
|
||||||
if key in signal_value:
|
|
||||||
account_values.add(Account(signal_value[key]))
|
|
||||||
|
|
||||||
account_values.add(Account(self.account))
|
|
||||||
printnow("Distinct account values:", account_values)
|
|
||||||
return account_values
|
|
||||||
|
|
||||||
#prdelat queue na dynamic - podle toho jak bud uchtit pracovat s multiresolutions
|
#prdelat queue na dynamic - podle toho jak bud uchtit pracovat s multiresolutions
|
||||||
#zatim jen jedna q1
|
#zatim jen jedna q1
|
||||||
#TODO zaroven strategie musi vedet o rectypu, protoze je zpracovava
|
#TODO zaroven strategie musi vedet o rectypu, protoze je zpracovava
|
||||||
@ -140,33 +116,25 @@ class Strategy:
|
|||||||
return -1
|
return -1
|
||||||
|
|
||||||
self.debug = debug
|
self.debug = debug
|
||||||
|
self.key = get_key(mode=mode, account=self.account)
|
||||||
|
|
||||||
if mode == Mode.LIVE or mode == Mode.PAPER:
|
if mode == Mode.LIVE or mode == Mode.PAPER:
|
||||||
#data loader thread
|
#data loader thread
|
||||||
self.dataloader = Trade_WS_Streamer(name="WS-LDR-"+self.name)
|
self.dataloader = Trade_WS_Streamer(name="WS-LDR-"+self.name)
|
||||||
#populate interfaces for each account
|
self.interface = LiveInterface(symbol=self.symbol, key=self.key)
|
||||||
for account in self.accounts:
|
# order notif thread
|
||||||
#get key for account
|
self.order_notifs = LiveOrderUpdatesStreamer(key=self.key, name="WS-STRMR-" + self.name)
|
||||||
key = get_key(mode=mode, account=Account(account))
|
#propojujeme notifice s interfacem (pro callback)
|
||||||
self.interface[account.name] = LiveInterface(symbol=self.symbol, key=key)
|
self.order_notifs.connect_callback(self)
|
||||||
# order notif thread
|
self.state = StrategyState(name=self.name, symbol = self.symbol, stratvars = self.stratvars, interface=self.interface, rectype=self.rectype, runner_id=self.runner_id, ilog_save=self.ilog_save)
|
||||||
self.order_notifs[account.name] = LiveOrderUpdatesStreamer(key=key, name="WS-STRMR-" + account.name + "-" + self.name, account=account)
|
|
||||||
#propojujeme notifice s interfacem (pro callback)
|
|
||||||
self.order_notifs[account.name].connect_callback(self)
|
|
||||||
|
|
||||||
self.state = StrategyState(name=self.name, accounts=self.accounts, account=self.account, symbol = self.symbol, stratvars = self.stratvars, interface=self.interface, rectype=self.rectype, runner_id=self.runner_id, ilog_save=self.ilog_save)
|
|
||||||
|
|
||||||
elif mode == Mode.BT:
|
elif mode == Mode.BT:
|
||||||
|
|
||||||
self.dataloader = Trade_Offline_Streamer(start, end, btdata=self.btdata)
|
self.dataloader = Trade_Offline_Streamer(start, end, btdata=self.btdata)
|
||||||
self.bt = Backtester(symbol = self.symbol, accounts=self.accounts, order_fill_callback= self.order_updates, btdata=self.btdata, cash=cash, bp_from=start, bp_to=end)
|
self.bt = Backtester(symbol = self.symbol, order_fill_callback= self.order_updates, btdata=self.btdata, cash=cash, bp_from=start, bp_to=end)
|
||||||
|
|
||||||
#populate interfaces for each account
|
self.interface = BacktestInterface(symbol=self.symbol, bt=self.bt)
|
||||||
for account in self.accounts:
|
self.state = StrategyState(name=self.name, symbol = self.symbol, stratvars = self.stratvars, interface=self.interface, rectype=self.rectype, runner_id=self.runner_id, bt=self.bt, ilog_save=self.ilog_save)
|
||||||
#pro backtest volame stejne oklicujeme interface
|
|
||||||
self.interface[account.name] = BacktestInterface(symbol=self.symbol, bt=self.bt, account=account)
|
|
||||||
self.state = StrategyState(name=self.name, accounts=self.accounts, account=self.account, symbol = self.symbol, stratvars = self.stratvars, interface=self.interface, rectype=self.rectype, runner_id=self.runner_id, bt=self.bt, ilog_save=self.ilog_save)
|
|
||||||
#no callback from bt, it is called directly
|
|
||||||
self.order_notifs = None
|
self.order_notifs = None
|
||||||
|
|
||||||
##streamer bude plnit trady do listu trades - nad kterym bude pracovat paper trade
|
##streamer bude plnit trady do listu trades - nad kterym bude pracovat paper trade
|
||||||
@ -174,10 +142,10 @@ class Strategy:
|
|||||||
self.dataloader.add_stream(TradeAggregator2List(symbol=self.symbol,btdata=self.btdata,rectype=RecordType.TRADE))
|
self.dataloader.add_stream(TradeAggregator2List(symbol=self.symbol,btdata=self.btdata,rectype=RecordType.TRADE))
|
||||||
elif mode == Mode.PREP:
|
elif mode == Mode.PREP:
|
||||||
#bt je zde jen pro udrzeni BT casu v logu atp. JInak jej nepouzivame.
|
#bt je zde jen pro udrzeni BT casu v logu atp. JInak jej nepouzivame.
|
||||||
self.bt = Backtester(symbol = self.symbol, accounts=self.accounts, order_fill_callback= self.order_updates, btdata=self.btdata, cash=cash, bp_from=start, bp_to=end)
|
self.bt = Backtester(symbol = self.symbol, order_fill_callback= self.order_updates, btdata=self.btdata, cash=cash, bp_from=start, bp_to=end)
|
||||||
self.interface = None
|
self.interface = None
|
||||||
#self.interface = BacktestInterface(symbol=self.symbol, bt=self.bt)
|
#self.interface = BacktestInterface(symbol=self.symbol, bt=self.bt)
|
||||||
self.state = StrategyState(name=self.name, accounts=self.accounts, account=self.account, symbol = self.symbol, stratvars = self.stratvars, interface=self.interface, rectype=self.rectype, runner_id=self.runner_id, bt=self.bt, ilog_save=self.ilog_save)
|
self.state = StrategyState(name=self.name, symbol = self.symbol, stratvars = self.stratvars, interface=self.interface, rectype=self.rectype, runner_id=self.runner_id, bt=self.bt, ilog_save=self.ilog_save)
|
||||||
self.order_notifs = None
|
self.order_notifs = None
|
||||||
|
|
||||||
else:
|
else:
|
||||||
@ -346,15 +314,13 @@ class Strategy:
|
|||||||
""""refresh positions and avgp - for CBAR once per confirmed, for BARS each time"""
|
""""refresh positions and avgp - for CBAR once per confirmed, for BARS each time"""
|
||||||
def refresh_positions(self, item):
|
def refresh_positions(self, item):
|
||||||
if self.rectype == RecordType.BAR:
|
if self.rectype == RecordType.BAR:
|
||||||
for account in self.accounts:
|
a,p = self.interface.pos()
|
||||||
a,p = self.interface[account.name].pos()
|
if a != -1:
|
||||||
if a != -1:
|
self.state.avgp, self.state.positions = a,p
|
||||||
self.state.account_variables[account.name].avgp, self.state.account_variables[account.name].positions = a, p
|
|
||||||
elif self.rectype in (RecordType.CBAR, RecordType.CBARVOLUME, RecordType.CBARDOLLAR, RecordType.CBARRENKO) and item['confirmed'] == 1:
|
elif self.rectype in (RecordType.CBAR, RecordType.CBARVOLUME, RecordType.CBARDOLLAR, RecordType.CBARRENKO) and item['confirmed'] == 1:
|
||||||
for account in self.accounts:
|
a,p = self.interface.pos()
|
||||||
a,p = self.interface[account.name].pos()
|
if a != -1:
|
||||||
if a != -1:
|
self.state.avgp, self.state.positions = a,p
|
||||||
self.state.account_variables[account.name].avgp, self.state.account_variables[account.name].positions = a, p
|
|
||||||
|
|
||||||
"""update state.last_trade_time a time of iteration"""
|
"""update state.last_trade_time a time of iteration"""
|
||||||
def update_times(self, item):
|
def update_times(self, item):
|
||||||
@ -450,11 +416,7 @@ class Strategy:
|
|||||||
|
|
||||||
if self.mode == Mode.LIVE or self.mode == Mode.PAPER:
|
if self.mode == Mode.LIVE or self.mode == Mode.PAPER:
|
||||||
#live notification thread
|
#live notification thread
|
||||||
#for all keys in self.order_notifs call start()
|
self.order_notifs.start()
|
||||||
for key in self.order_notifs:
|
|
||||||
self.order_notifs[key].start()
|
|
||||||
|
|
||||||
#self.order_notifs.start()
|
|
||||||
elif self.mode == Mode.BT or self.mode == Mode.PREP:
|
elif self.mode == Mode.BT or self.mode == Mode.PREP:
|
||||||
self.bt.backtest_start = datetime.now()
|
self.bt.backtest_start = datetime.now()
|
||||||
|
|
||||||
@ -524,7 +486,7 @@ class Strategy:
|
|||||||
self.stop()
|
self.stop()
|
||||||
|
|
||||||
if self.mode == Mode.BT:
|
if self.mode == Mode.BT:
|
||||||
print("REQUEST COUNT:", {account_str:self.interface[account_str].mincnt for account_str in self.interface})
|
print("REQUEST COUNT:", self.interface.mincnt)
|
||||||
|
|
||||||
self.bt.backtest_end = datetime.now()
|
self.bt.backtest_end = datetime.now()
|
||||||
#print(40*"*",self.mode, "BACKTEST RESULTS",40*"*")
|
#print(40*"*",self.mode, "BACKTEST RESULTS",40*"*")
|
||||||
@ -538,9 +500,7 @@ class Strategy:
|
|||||||
|
|
||||||
#disconnect strategy from websocket trader updates
|
#disconnect strategy from websocket trader updates
|
||||||
if self.mode == Mode.LIVE or self.mode == Mode.PAPER:
|
if self.mode == Mode.LIVE or self.mode == Mode.PAPER:
|
||||||
for key in self.order_notifs:
|
self.order_notifs.disconnect_callback(self)
|
||||||
self.order_notifs[key].disconnect_callback(self)
|
|
||||||
#self.order_notifs.disconnect_callback(self)
|
|
||||||
|
|
||||||
#necessary only for shared loaders (to keep it running for other stratefies)
|
#necessary only for shared loaders (to keep it running for other stratefies)
|
||||||
for i in self._streams:
|
for i in self._streams:
|
||||||
@ -581,11 +541,11 @@ class Strategy:
|
|||||||
#for order updates from LIVE or BACKTEST
|
#for order updates from LIVE or BACKTEST
|
||||||
#updates are sent only for SYMBOL of strategy
|
#updates are sent only for SYMBOL of strategy
|
||||||
|
|
||||||
async def order_updates(self, data: TradeUpdate, account: Account):
|
async def order_updates(self, data: TradeUpdate):
|
||||||
if self.mode == Mode.LIVE or self.mode == Mode.PAPER:
|
if self.mode == Mode.LIVE or self.mode == Mode.PAPER:
|
||||||
now = datetime.now().timestamp()
|
now = datetime.now().timestamp()
|
||||||
#z alpakýho TradeEvent si udelame svuj rozsireny TradeEvent (obsahujici navic profit atp.)
|
#z alpakýho TradeEvent si udelame svuj rozsireny TradeEvent (obsahujici navic profit atp.)
|
||||||
data = TradeUpdate(**data.dict(), account=account)
|
data = TradeUpdate(**data.dict())
|
||||||
else:
|
else:
|
||||||
now = self.bt.time
|
now = self.bt.time
|
||||||
|
|
||||||
@ -672,14 +632,17 @@ class Strategy:
|
|||||||
rt_out["statinds"] = dict()
|
rt_out["statinds"] = dict()
|
||||||
for key, value in self.state.statinds.items():
|
for key, value in self.state.statinds.items():
|
||||||
rt_out["statinds"][key] = value
|
rt_out["statinds"][key] = value
|
||||||
|
|
||||||
|
#vkladame average price and positions, pokud existuji
|
||||||
|
#self.state.avgp , self.state.positions
|
||||||
|
|
||||||
#pro typ strategie Classic, posilame i vysi stoploss
|
#pro typ strategie Classic, posilame i vysi stoploss
|
||||||
try:
|
try:
|
||||||
sl_value = gaka(self.state.account_variables, "activeTrade", lambda x: x.stoploss_value)
|
sl_value = self.state.vars["activeTrade"].stoploss_value
|
||||||
except (KeyError, AttributeError):
|
except (KeyError, AttributeError):
|
||||||
sl_value = None
|
sl_value = None
|
||||||
|
|
||||||
rt_out["positions"] = dict(time=self.state.time, positions=gaka(self.state.account_variables, "positions"), avgp=gaka(self.state.account_variables,), sl_value=sl_value)
|
rt_out["positions"] = dict(time=self.state.time, positions=self.state.positions, avgp=self.state.avgp, sl_value=sl_value)
|
||||||
|
|
||||||
#vkladame limitku a pendingbuys
|
#vkladame limitku a pendingbuys
|
||||||
try:
|
try:
|
||||||
@ -755,21 +718,17 @@ class StrategyState:
|
|||||||
"""Strategy Stat object that is passed to callbacks
|
"""Strategy Stat object that is passed to callbacks
|
||||||
note:
|
note:
|
||||||
state.time
|
state.time
|
||||||
state.interface[account.name].time
|
state.interface.time
|
||||||
accounts = set of all accounts (strings)
|
|
||||||
account = enum of primary account (Account)
|
|
||||||
většinou mají stejnou hodnotu, ale lišit se mužou např. v případě BT callbacku - kdy se v rámci okna končící state.time realizují objednávky, které
|
většinou mají stejnou hodnotu, ale lišit se mužou např. v případě BT callbacku - kdy se v rámci okna končící state.time realizují objednávky, které
|
||||||
triggerují callback, který následně vyvolá např. buy (ten se musí ale udít v čase fillu, tzn. callback si nastaví čas interfacu na filltime)
|
triggerují callback, který následně vyvolá např. buy (ten se musí ale udít v čase fillu, tzn. callback si nastaví čas interfacu na filltime)
|
||||||
po dokončení bt kroků před zahájením iterace "NEXT" se časy znovu updatnout na původni state.time
|
po dokončení bt kroků před zahájením iterace "NEXT" se časy znovu updatnout na původni state.time
|
||||||
"""
|
"""
|
||||||
def __init__(self, name: str, symbol: str, accounts: set, account: Account, stratvars: AttributeDict, bars: AttributeDict = {}, trades: AttributeDict = {}, interface: GeneralInterface = None, rectype: RecordType = RecordType.BAR, runner_id: UUID = None, bt: Backtester = None, ilog_save: bool = False):
|
def __init__(self, name: str, symbol: str, stratvars: AttributeDict, bars: AttributeDict = {}, trades: AttributeDict = {}, interface: GeneralInterface = None, rectype: RecordType = RecordType.BAR, runner_id: UUID = None, bt: Backtester = None, ilog_save: bool = False):
|
||||||
self.vars = stratvars
|
self.vars = stratvars
|
||||||
self.interface = interface
|
self.interface = interface
|
||||||
self.account = account #primary account
|
self.positions = 0
|
||||||
self.accounts = accounts
|
self.avgp = 0
|
||||||
#populate account variables dictionary
|
self.blockbuy = 0
|
||||||
self.account_variables: Dict[str, AccountVariables] = {account.name: AccountVariables() for account in self.accounts}
|
|
||||||
|
|
||||||
self.name = name
|
self.name = name
|
||||||
self.symbol = symbol
|
self.symbol = symbol
|
||||||
self.rectype = rectype
|
self.rectype = rectype
|
||||||
@ -778,10 +737,11 @@ class StrategyState:
|
|||||||
self.time = None
|
self.time = None
|
||||||
#time of last trade processed
|
#time of last trade processed
|
||||||
self.last_trade_time = 0
|
self.last_trade_time = 0
|
||||||
self.last_entry_price={key:dict(long=0,short=999) for key in self.accounts}
|
self.last_entry_price=dict(long=0,short=999)
|
||||||
self.resolution = None
|
self.resolution = None
|
||||||
self.runner_id = runner_id
|
self.runner_id = runner_id
|
||||||
self.bt = bt
|
self.bt = bt
|
||||||
|
self.dont_exit_already_activated = False
|
||||||
self.docasny_rel_profit = []
|
self.docasny_rel_profit = []
|
||||||
self.ilog_save = ilog_save
|
self.ilog_save = ilog_save
|
||||||
self.sl_optimizer_short = optimsl.SLOptimizer(ptm.TradeDirection.SHORT)
|
self.sl_optimizer_short = optimsl.SLOptimizer(ptm.TradeDirection.SHORT)
|
||||||
@ -819,14 +779,18 @@ class StrategyState:
|
|||||||
#secondary resolution indicators
|
#secondary resolution indicators
|
||||||
#self.secondary_indicators = AttributeDict(time=[], sec_price=[])
|
#self.secondary_indicators = AttributeDict(time=[], sec_price=[])
|
||||||
self.statinds = AttributeDict()
|
self.statinds = AttributeDict()
|
||||||
|
#these methods can be overrided by StrategyType (to add or alter its functionality)
|
||||||
|
self.buy = self.interface.buy
|
||||||
|
self.buy_l = self.interface.buy_l
|
||||||
|
self.sell = self.interface.sell
|
||||||
|
self.sell_l = self.interface.sell_l
|
||||||
self.cancel_pending_buys = None
|
self.cancel_pending_buys = None
|
||||||
self.iter_log_list = []
|
self.iter_log_list = []
|
||||||
self.dailyBars = defaultdict(dict)
|
self.dailyBars = defaultdict(dict)
|
||||||
#celkovy profit (prejmennovat na profit_cum)
|
#celkovy profit (prejmennovat na profit_cum)
|
||||||
self.profit = 0 #TODO key by account?
|
self.profit = 0
|
||||||
#celkovy relativni profit (obsahuje pole relativnich zisku, z jeho meanu se spocita celkovy rel_profit_cu,)
|
#celkovy relativni profit (obsahuje pole relativnich zisku, z jeho meanu se spocita celkovy rel_profit_cu,)
|
||||||
self.rel_profit_cum = []#TODO key by account?
|
self.rel_profit_cum = []
|
||||||
self.tradeList = []
|
self.tradeList = []
|
||||||
#nova promenna pro externi data do ArchiveDetaili, napr. pro zobrazeni v grafu, je zde např. SL history
|
#nova promenna pro externi data do ArchiveDetaili, napr. pro zobrazeni v grafu, je zde např. SL history
|
||||||
self.extData = defaultdict(dict)
|
self.extData = defaultdict(dict)
|
||||||
@ -835,25 +799,6 @@ class StrategyState:
|
|||||||
self.today_market_close = None
|
self.today_market_close = None
|
||||||
self.classed_indicators = {}
|
self.classed_indicators = {}
|
||||||
|
|
||||||
#quick interface actions to access from state without having to write interface[account.name].buy_l
|
|
||||||
def buy_l(self, account: Account, price: float, size: int = 1, repeat: bool = False, force: int = 0):
|
|
||||||
self.interface[account.name].buy_l(price, size, repeat, force)
|
|
||||||
|
|
||||||
def buy(self, account: Account, size = 1, repeat: bool = False):
|
|
||||||
self.interface[account.name].buy(size, repeat)
|
|
||||||
|
|
||||||
def sell_l(self, account: Account, price: float, size: int = 1, repeat: bool = False):
|
|
||||||
self.interface[account.name].sell_l(price, size, repeat)
|
|
||||||
|
|
||||||
def sell(self, account: Account, size = 1, repeat: bool = False):
|
|
||||||
self.interface[account.name].sell(size, repeat)
|
|
||||||
|
|
||||||
def repl(self, account: Account, orderid: str, price: float = None, size: int = 1, repeat: bool = False):
|
|
||||||
self.interface[account.name].repl(orderid, price, size, repeat)
|
|
||||||
|
|
||||||
def cancel(self, account: Account, orderid: str):
|
|
||||||
self.interface[account.name].cancel(orderid)
|
|
||||||
|
|
||||||
def release(self):
|
def release(self):
|
||||||
#release large variables
|
#release large variables
|
||||||
self.bars = None
|
self.bars = None
|
||||||
|
|||||||
@ -1,13 +1,9 @@
|
|||||||
from v2realbot.strategyblocks.activetrade.sl.trailsl import trail_SL_management
|
from v2realbot.strategyblocks.activetrade.sl.trailsl import trail_SL_management
|
||||||
from v2realbot.strategyblocks.activetrade.close.evaluate_close import eval_close_position
|
from v2realbot.strategyblocks.activetrade.close.evaluate_close import eval_close_position
|
||||||
from v2realbot.utils.utils import gaka
|
|
||||||
def manage_active_trade(state, data):
|
|
||||||
accountsWithActiveTrade = gaka(state.account_variables, "activeTrade", None, lambda x: x is not None)
|
|
||||||
# {"account1": activeTrade,
|
|
||||||
# "account2": activeTrade}
|
|
||||||
|
|
||||||
if len(accountsWithActiveTrade.values()) == 0:
|
def manage_active_trade(state, data):
|
||||||
return
|
trade = state.vars.activeTrade
|
||||||
|
if trade is None:
|
||||||
trail_SL_management(state, accountsWithActiveTrade, data)
|
return -1
|
||||||
eval_close_position(state, accountsWithActiveTrade, data)
|
trail_SL_management(state, data)
|
||||||
|
eval_close_position(state, data)
|
||||||
@ -1,7 +1,7 @@
|
|||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
|
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
|
||||||
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
|
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
|
||||||
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
|
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff, create_new_bars, slice_dict_lists
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff, create_new_bars, slice_dict_lists
|
||||||
from v2realbot.utils.directive_utils import get_conditions_from_configuration
|
from v2realbot.utils.directive_utils import get_conditions_from_configuration
|
||||||
from v2realbot.common.model import SLHistory
|
from v2realbot.common.model import SLHistory
|
||||||
@ -18,61 +18,52 @@ import os
|
|||||||
from traceback import format_exc
|
from traceback import format_exc
|
||||||
from v2realbot.strategyblocks.activetrade.helpers import insert_SL_history
|
from v2realbot.strategyblocks.activetrade.helpers import insert_SL_history
|
||||||
|
|
||||||
#TODO tady jsem taky skoncil a pak zpetna evaluate_close (mozna zde staci jen account?)
|
|
||||||
|
|
||||||
# - close means change status in prescribed Trends,update profit, delete from activeTrade
|
# - close means change status in prescribed Trends,update profit, delete from activeTrade
|
||||||
def close_position(state: StrategyState, activeTrade: Trade, data, direction: TradeDirection, reason: str, followup: Followup = None):
|
def close_position(state, data, direction: TradeDirection, reason: str, followup: Followup = None):
|
||||||
followup_text = str(followup) if followup is not None else ""
|
followup_text = str(followup) if followup is not None else ""
|
||||||
positions = state.account_variables[activeTrade.account.name].positions
|
state.ilog(lvl=1,e=f"CLOSING TRADE {followup_text} {reason} {str(direction)}", curr_price=data["close"], trade=state.vars.activeTrade)
|
||||||
state.ilog(lvl=1,e=f"CLOSING TRADE {followup_text} {reason} {str(direction)}", curr_price=data["close"], trade=activeTrade)
|
|
||||||
if direction == TradeDirection.SHORT:
|
if direction == TradeDirection.SHORT:
|
||||||
res = state.buy(account=activeTrade.account, size=abs(int(positions)))
|
res = state.buy(size=abs(int(state.positions)))
|
||||||
if isinstance(res, int) and res < 0:
|
if isinstance(res, int) and res < 0:
|
||||||
raise Exception(f"error in required operation {reason} {res}")
|
raise Exception(f"error in required operation {reason} {res}")
|
||||||
|
|
||||||
elif direction == TradeDirection.LONG:
|
elif direction == TradeDirection.LONG:
|
||||||
res = state.sell(account=activeTrade.account, size=positions)
|
res = state.sell(size=state.positions)
|
||||||
if isinstance(res, int) and res < 0:
|
if isinstance(res, int) and res < 0:
|
||||||
raise Exception(f"error in required operation STOPLOSS SELL {res}") #TBD error handling
|
raise Exception(f"error in required operation STOPLOSS SELL {res}")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise Exception(f"unknow TradeDirection in close_position")
|
raise Exception(f"unknow TradeDirection in close_position")
|
||||||
|
|
||||||
#pri uzavreni tradu zapisujeme SL history - lepsi zorbazeni v grafu
|
#pri uzavreni tradu zapisujeme SL history - lepsi zorbazeni v grafu
|
||||||
insert_SL_history(state, activeTrade)
|
insert_SL_history(state)
|
||||||
state.account_variables[activeTrade.account.name].pending = activeTrade.id
|
state.dont_exit_already_activated = False
|
||||||
state.account_variables[activeTrade.account.name].activeTrade = None
|
state.vars.pending = state.vars.activeTrade.id
|
||||||
#state.account_variables[activeTrade.account.name].last_exit_index = data["index"]
|
state.vars.activeTrade = None
|
||||||
state.vars.last_exit_index = data["index"]
|
state.vars.last_exit_index = data["index"]
|
||||||
state.account_variables[activeTrade.account.name].dont_exit_already_activated = False
|
|
||||||
if followup is not None:
|
if followup is not None:
|
||||||
state.account_variables[activeTrade.account.name].requested_followup = followup
|
state.vars.requested_followup = followup
|
||||||
|
|
||||||
#close only partial position - no followup here, size multiplier must be between 0 and 1
|
#close only partial position - no followup here, size multiplier must be between 0 and 1
|
||||||
def close_position_partial(state, activeTrade: Trade,data, direction: TradeDirection, reason: str, size: float):
|
def close_position_partial(state, data, direction: TradeDirection, reason: str, size: float):
|
||||||
positions = state.account_variables[activeTrade.account.name].positions
|
|
||||||
if size <= 0 or size >=1:
|
if size <= 0 or size >=1:
|
||||||
raise Exception(f"size must be betweem 0 and 1")
|
raise Exception(f"size must be betweem 0 and 1")
|
||||||
size_abs = abs(int(int(positions)*size))
|
size_abs = abs(int(int(state.positions)*size))
|
||||||
state.ilog(lvl=1,e=f"CLOSING TRADE PART: {size_abs} {size} {reason} {str(direction)}", curr_price=data["close"], trade=activeTrade)
|
state.ilog(lvl=1,e=f"CLOSING TRADE PART: {size_abs} {size} {reason} {str(direction)}", curr_price=data["close"], trade=state.vars.activeTrade)
|
||||||
if direction == TradeDirection.SHORT:
|
if direction == TradeDirection.SHORT:
|
||||||
res = state.buy(account=activeTrade.account, size=size_abs)
|
res = state.buy(size=size_abs)
|
||||||
if isinstance(res, int) and res < 0:
|
if isinstance(res, int) and res < 0:
|
||||||
raise Exception(f"error in required operation STOPLOSS PARTIAL BUY {reason} {res}")
|
raise Exception(f"error in required operation STOPLOSS PARTIAL BUY {reason} {res}")
|
||||||
|
|
||||||
elif direction == TradeDirection.LONG:
|
elif direction == TradeDirection.LONG:
|
||||||
res = state.sell(account=activeTrade.account, size=size_abs)
|
res = state.sell(size=size_abs)
|
||||||
if isinstance(res, int) and res < 0:
|
if isinstance(res, int) and res < 0:
|
||||||
raise Exception(f"error in required operation STOPLOSS PARTIAL SELL {res}")
|
raise Exception(f"error in required operation STOPLOSS PARTIAL SELL {res}")
|
||||||
else:
|
else:
|
||||||
raise Exception(f"unknow TradeDirection in close_position")
|
raise Exception(f"unknow TradeDirection in close_position")
|
||||||
|
|
||||||
#pri uzavreni tradu zapisujeme SL history - lepsi zorbazeni v grafu
|
#pri uzavreni tradu zapisujeme SL history - lepsi zorbazeni v grafu
|
||||||
insert_SL_history(state, activeTrade)
|
insert_SL_history(state)
|
||||||
state.account_variables[activeTrade.account.name].pending = activeTrade.id
|
state.vars.pending = state.vars.activeTrade.id
|
||||||
state.account_variables[activeTrade.account.name].activeTrade = None
|
|
||||||
state.account_variables[activeTrade.account.name].dont_exit_already_activated = False
|
|
||||||
#state.account_variables[activeTrade.account.name].last_exit_index = data["index"]
|
|
||||||
|
|
||||||
#state.vars.activeTrade = None
|
#state.vars.activeTrade = None
|
||||||
state.vars.last_exit_index = data["index"] #ponechano mimo account
|
#state.vars.last_exit_index = data["index"]
|
||||||
@ -1,7 +1,7 @@
|
|||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
|
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
|
||||||
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
|
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
|
||||||
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
|
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff, create_new_bars, slice_dict_lists
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff, create_new_bars, slice_dict_lists
|
||||||
from v2realbot.utils.directive_utils import get_conditions_from_configuration
|
from v2realbot.utils.directive_utils import get_conditions_from_configuration
|
||||||
from v2realbot.common.model import SLHistory
|
from v2realbot.common.model import SLHistory
|
||||||
@ -12,9 +12,9 @@ from threading import Event
|
|||||||
import os
|
import os
|
||||||
from traceback import format_exc
|
from traceback import format_exc
|
||||||
from v2realbot.strategyblocks.indicators.helpers import evaluate_directive_conditions
|
from v2realbot.strategyblocks.indicators.helpers import evaluate_directive_conditions
|
||||||
from v2realbot.strategyblocks.activetrade.helpers import get_signal_section_directive, normalize_tick
|
from v2realbot.strategyblocks.activetrade.helpers import get_override_for_active_trade, normalize_tick
|
||||||
|
|
||||||
def dontexit_protection_met(state, activeTrade: Trade, data, direction: TradeDirection):
|
def dontexit_protection_met(state, data, direction: TradeDirection):
|
||||||
if direction == TradeDirection.LONG:
|
if direction == TradeDirection.LONG:
|
||||||
smer = "long"
|
smer = "long"
|
||||||
else:
|
else:
|
||||||
@ -24,64 +24,58 @@ def dontexit_protection_met(state, activeTrade: Trade, data, direction: TradeDir
|
|||||||
#vyreseno pri kazde aktivaci se vyplni flag already_activated
|
#vyreseno pri kazde aktivaci se vyplni flag already_activated
|
||||||
#pri naslednem false podminky se v pripade, ze je aktivovany flag posle True -
|
#pri naslednem false podminky se v pripade, ze je aktivovany flag posle True -
|
||||||
#take se vyrusi v closu
|
#take se vyrusi v closu
|
||||||
def process_result(result, account):
|
def process_result(result):
|
||||||
if result:
|
if result:
|
||||||
state.account_variables[account.name].dont_exit_already_activated = True
|
state.dont_exit_already_activated = True
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def evaluate_result():
|
def evaluate_result():
|
||||||
mother_signal = activeTrade.generated_by
|
mother_signal = state.vars.activeTrade.generated_by
|
||||||
dont_exit_already_activated = state.account_variables[activeTrade.account.name].dont_exit_already_activated
|
|
||||||
|
|
||||||
if mother_signal is not None:
|
if mother_signal is not None:
|
||||||
#TESTUJEME DONT_EXIT_
|
#TESTUJEME DONT_EXIT_
|
||||||
cond_dict = state.vars.conditions[KW.dont_exit][mother_signal][smer]
|
cond_dict = state.vars.conditions[KW.dont_exit][mother_signal][smer]
|
||||||
#OR
|
#OR
|
||||||
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "OR")
|
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "OR")
|
||||||
state.ilog(lvl=1,e=f"DONT_EXIT {mother_signal} {smer} =OR= {result}", **conditions_met, cond_dict=cond_dict, already_activated=str(dont_exit_already_activated))
|
state.ilog(lvl=1,e=f"DONT_EXIT {mother_signal} {smer} =OR= {result}", **conditions_met, cond_dict=cond_dict, already_activated=str(state.dont_exit_already_activated))
|
||||||
if result:
|
if result:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
#OR neprosly testujeme AND
|
#OR neprosly testujeme AND
|
||||||
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "AND")
|
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "AND")
|
||||||
state.ilog(lvl=1,e=f"DONT_EXIT {mother_signal} {smer} =AND= {result}", **conditions_met, cond_dict=cond_dict, already_activated=str(dont_exit_already_activated))
|
state.ilog(lvl=1,e=f"DONT_EXIT {mother_signal} {smer} =AND= {result}", **conditions_met, cond_dict=cond_dict, already_activated=str(state.dont_exit_already_activated))
|
||||||
if result:
|
if result:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
cond_dict = state.vars.conditions[KW.dont_exit]["common"][smer]
|
cond_dict = state.vars.conditions[KW.dont_exit]["common"][smer]
|
||||||
#OR
|
#OR
|
||||||
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "OR")
|
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "OR")
|
||||||
state.ilog(lvl=1,e=f"DONT_EXIT common {smer} =OR= {result}", **conditions_met, cond_dict=cond_dict, already_activated=str(dont_exit_already_activated))
|
state.ilog(lvl=1,e=f"DONT_EXIT common {smer} =OR= {result}", **conditions_met, cond_dict=cond_dict, already_activated=str(state.dont_exit_already_activated))
|
||||||
if result:
|
if result:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
#OR neprosly testujeme AND
|
#OR neprosly testujeme AND
|
||||||
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "AND")
|
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "AND")
|
||||||
state.ilog(lvl=1,e=f"DONT_EXIT common {smer} =AND= {result}", **conditions_met, cond_dict=cond_dict, already_activated=str(dont_exit_already_activated))
|
state.ilog(lvl=1,e=f"DONT_EXIT common {smer} =AND= {result}", **conditions_met, cond_dict=cond_dict, already_activated=str(state.dont_exit_already_activated))
|
||||||
return result
|
return result
|
||||||
|
|
||||||
#nejprve evaluujeme vsechny podminky
|
#nejprve evaluujeme vsechny podminky
|
||||||
result = evaluate_result()
|
result = evaluate_result()
|
||||||
|
|
||||||
#pak evaluujeme vysledek a vracíme
|
#pak evaluujeme vysledek a vracíme
|
||||||
return process_result(result, activeTrade.account)
|
return process_result(result)
|
||||||
|
|
||||||
|
|
||||||
def exit_conditions_met(state: StrategyState, activeTrade: Trade, data, direction: TradeDirection):
|
def exit_conditions_met(state, data, direction: TradeDirection):
|
||||||
if direction == TradeDirection.LONG:
|
if direction == TradeDirection.LONG:
|
||||||
smer = "long"
|
smer = "long"
|
||||||
else:
|
else:
|
||||||
smer = "short"
|
smer = "short"
|
||||||
|
|
||||||
signal_name = activeTrade.generated_by
|
|
||||||
last_entry_index = state.account_variables[activeTrade.account.name].last_entry_index
|
|
||||||
avgp = state.account_variables[activeTrade.account.name].avgp
|
|
||||||
positions = state.account_variables[activeTrade.account.name].positions
|
|
||||||
|
|
||||||
directive_name = "exit_cond_only_on_confirmed"
|
directive_name = "exit_cond_only_on_confirmed"
|
||||||
exit_cond_only_on_confirmed = get_signal_section_directive(state, signal_name=signal_name, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
exit_cond_only_on_confirmed = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
||||||
|
|
||||||
if exit_cond_only_on_confirmed and data['confirmed'] == 0:
|
if exit_cond_only_on_confirmed and data['confirmed'] == 0:
|
||||||
state.ilog(lvl=0,e="EXIT COND ONLY ON CONFIRMED BAR")
|
state.ilog(lvl=0,e="EXIT COND ONLY ON CONFIRMED BAR")
|
||||||
@ -89,20 +83,20 @@ def exit_conditions_met(state: StrategyState, activeTrade: Trade, data, directio
|
|||||||
|
|
||||||
## minimální počet barů od vstupu
|
## minimální počet barů od vstupu
|
||||||
directive_name = "exit_cond_req_bars"
|
directive_name = "exit_cond_req_bars"
|
||||||
exit_cond_req_bars = get_signal_section_directive(state, signal_name=signal_name, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, 1))
|
exit_cond_req_bars = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, 1))
|
||||||
|
|
||||||
if last_entry_index is not None:
|
if state.vars.last_in_index is not None:
|
||||||
index_to_compare = int(last_entry_index)+int(exit_cond_req_bars)
|
index_to_compare = int(state.vars.last_in_index)+int(exit_cond_req_bars)
|
||||||
if int(data["index"]) < index_to_compare:
|
if int(data["index"]) < index_to_compare:
|
||||||
state.ilog(lvl=1,e=f"EXIT COND WAITING on required bars from IN {exit_cond_req_bars} TOO SOON", currindex=data["index"], index_to_compare=index_to_compare, last_entry_index=last_entry_index)
|
state.ilog(lvl=1,e=f"EXIT COND WAITING on required bars from IN {exit_cond_req_bars} TOO SOON", currindex=data["index"], index_to_compare=index_to_compare, last_in_index=state.vars.last_in_index)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
#POKUD je nastaven MIN PROFIT, zkontrolujeme ho a az pripadne pustime CONDITIONY
|
#POKUD je nastaven MIN PROFIT, zkontrolujeme ho a az pripadne pustime CONDITIONY
|
||||||
directive_name = "exit_cond_min_profit"
|
directive_name = "exit_cond_min_profit"
|
||||||
exit_cond_min_profit_nodir = get_signal_section_directive(state, signal_name=signal_name, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
exit_cond_min_profit_nodir = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
||||||
|
|
||||||
directive_name = "exit_cond_min_profit_" + str(smer)
|
directive_name = "exit_cond_min_profit_" + str(smer)
|
||||||
exit_cond_min_profit = get_signal_section_directive(state, signal_name=signal_name,directive_name=directive_name, default_value=exit_cond_min_profit_nodir)
|
exit_cond_min_profit = get_override_for_active_trade(state, directive_name=directive_name, default_value=exit_cond_min_profit_nodir)
|
||||||
|
|
||||||
|
|
||||||
#máme nastavený exit_cond_min_profit
|
#máme nastavený exit_cond_min_profit
|
||||||
@ -111,10 +105,10 @@ def exit_conditions_met(state: StrategyState, activeTrade: Trade, data, directio
|
|||||||
|
|
||||||
if exit_cond_min_profit is not None:
|
if exit_cond_min_profit is not None:
|
||||||
exit_cond_min_profit_normalized = normalize_tick(state, data, float(exit_cond_min_profit))
|
exit_cond_min_profit_normalized = normalize_tick(state, data, float(exit_cond_min_profit))
|
||||||
exit_cond_goal_price = price2dec(float(avgp)+exit_cond_min_profit_normalized,3) if int(positions) > 0 else price2dec(float(avgp)-exit_cond_min_profit_normalized,3)
|
exit_cond_goal_price = price2dec(float(state.avgp)+exit_cond_min_profit_normalized,3) if int(state.positions) > 0 else price2dec(float(state.avgp)-exit_cond_min_profit_normalized,3)
|
||||||
curr_price = float(data["close"])
|
curr_price = float(data["close"])
|
||||||
state.ilog(lvl=1,e=f"EXIT COND min profit {exit_cond_goal_price=} {exit_cond_min_profit=} {exit_cond_min_profit_normalized=} {curr_price=}")
|
state.ilog(lvl=1,e=f"EXIT COND min profit {exit_cond_goal_price=} {exit_cond_min_profit=} {exit_cond_min_profit_normalized=} {curr_price=}")
|
||||||
if (int(positions) < 0 and curr_price<=exit_cond_goal_price) or (int(positions) > 0 and curr_price>=exit_cond_goal_price):
|
if (int(state.positions) < 0 and curr_price<=exit_cond_goal_price) or (int(state.positions) > 0 and curr_price>=exit_cond_goal_price):
|
||||||
state.ilog(lvl=1,e=f"EXIT COND min profit PASS - POKRACUJEME")
|
state.ilog(lvl=1,e=f"EXIT COND min profit PASS - POKRACUJEME")
|
||||||
else:
|
else:
|
||||||
state.ilog(lvl=1,e=f"EXIT COND min profit NOT PASS")
|
state.ilog(lvl=1,e=f"EXIT COND min profit NOT PASS")
|
||||||
@ -143,10 +137,10 @@ def exit_conditions_met(state: StrategyState, activeTrade: Trade, data, directio
|
|||||||
#bereme bud exit condition signalu, ktery activeTrade vygeneroval+ fallback na general
|
#bereme bud exit condition signalu, ktery activeTrade vygeneroval+ fallback na general
|
||||||
state.ilog(lvl=0,e=f"EXIT CONDITIONS ENTRY {smer}", conditions=state.vars.conditions[KW.exit])
|
state.ilog(lvl=0,e=f"EXIT CONDITIONS ENTRY {smer}", conditions=state.vars.conditions[KW.exit])
|
||||||
|
|
||||||
mother_signal = signal_name
|
mother_signal = state.vars.activeTrade.generated_by
|
||||||
|
|
||||||
if mother_signal is not None:
|
if mother_signal is not None:
|
||||||
cond_dict = state.vars.conditions[KW.exit][signal_name][smer]
|
cond_dict = state.vars.conditions[KW.exit][state.vars.activeTrade.generated_by][smer]
|
||||||
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "OR")
|
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "OR")
|
||||||
state.ilog(lvl=1,e=f"EXIT CONDITIONS of {mother_signal} =OR= {result}", **conditions_met, cond_dict=cond_dict)
|
state.ilog(lvl=1,e=f"EXIT CONDITIONS of {mother_signal} =OR= {result}", **conditions_met, cond_dict=cond_dict)
|
||||||
if result:
|
if result:
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
|
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
|
||||||
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
|
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
|
||||||
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
|
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff, create_new_bars, slice_dict_lists
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff, create_new_bars, slice_dict_lists
|
||||||
from v2realbot.utils.directive_utils import get_conditions_from_configuration
|
from v2realbot.utils.directive_utils import get_conditions_from_configuration
|
||||||
from v2realbot.common.model import SLHistory
|
from v2realbot.common.model import SLHistory
|
||||||
@ -18,10 +18,10 @@ import os
|
|||||||
from traceback import format_exc
|
from traceback import format_exc
|
||||||
from v2realbot.strategyblocks.activetrade.helpers import insert_SL_history
|
from v2realbot.strategyblocks.activetrade.helpers import insert_SL_history
|
||||||
from v2realbot.strategyblocks.activetrade.close.conditions import dontexit_protection_met, exit_conditions_met
|
from v2realbot.strategyblocks.activetrade.close.conditions import dontexit_protection_met, exit_conditions_met
|
||||||
from v2realbot.strategyblocks.activetrade.helpers import get_max_profit_price, get_profit_target_price, get_signal_section_directive
|
from v2realbot.strategyblocks.activetrade.helpers import get_max_profit_price, get_profit_target_price, get_override_for_active_trade, keyword_conditions_met
|
||||||
|
|
||||||
|
|
||||||
def eod_exit_activated(state: StrategyState, activeTrade: Trade, data, direction: TradeDirection):
|
def eod_exit_activated(state: StrategyState, data, direction: TradeDirection):
|
||||||
"""
|
"""
|
||||||
Function responsible for end of day management
|
Function responsible for end of day management
|
||||||
|
|
||||||
@ -38,10 +38,8 @@ def eod_exit_activated(state: StrategyState, activeTrade: Trade, data, direction
|
|||||||
- 1 min forced immediate
|
- 1 min forced immediate
|
||||||
"""
|
"""
|
||||||
|
|
||||||
avgp = state.account_variables[activeTrade.account.name].avgp
|
|
||||||
|
|
||||||
directive_name = "forced_exit_window_start"
|
directive_name = "forced_exit_window_start"
|
||||||
forced_exit_window_start = get_signal_section_directive(state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
forced_exit_window_start = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
||||||
|
|
||||||
if forced_exit_window_start is None:
|
if forced_exit_window_start is None:
|
||||||
state.ilog(lvl=0,e="Forced exit not required.")
|
state.ilog(lvl=0,e="Forced exit not required.")
|
||||||
@ -49,7 +47,7 @@ def eod_exit_activated(state: StrategyState, activeTrade: Trade, data, direction
|
|||||||
|
|
||||||
|
|
||||||
directive_name = "forced_exit_window_end"
|
directive_name = "forced_exit_window_end"
|
||||||
forced_exit_window_end = get_signal_section_directive(state, signal_name=activeTrade.generated_by,directive_name=directive_name, default_value=safe_get(state.vars, directive_name, 389))
|
forced_exit_window_end = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, 389))
|
||||||
|
|
||||||
if forced_exit_window_start>389:
|
if forced_exit_window_start>389:
|
||||||
state.ilog(lvl=0,e="Forced exit window end max is 389")
|
state.ilog(lvl=0,e="Forced exit window end max is 389")
|
||||||
@ -62,7 +60,7 @@ def eod_exit_activated(state: StrategyState, activeTrade: Trade, data, direction
|
|||||||
|
|
||||||
# #dokdy konci okno snizujiciho se profitu (zbytek je breakeven a posledni minuta forced) - default pulka okna
|
# #dokdy konci okno snizujiciho se profitu (zbytek je breakeven a posledni minuta forced) - default pulka okna
|
||||||
# directive_name = "forced_exit_decreasing_profit_window_end"
|
# directive_name = "forced_exit_decreasing_profit_window_end"
|
||||||
# forced_exit_decreasing_profit_window_end = get_signal_section_directive(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, (forced_exit_window_end-forced_exit_window_end)/2))
|
# forced_exit_decreasing_profit_window_end = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, (forced_exit_window_end-forced_exit_window_end)/2))
|
||||||
|
|
||||||
# if forced_exit_decreasing_profit_window_end > forced_exit_window_end-1:
|
# if forced_exit_decreasing_profit_window_end > forced_exit_window_end-1:
|
||||||
# state.ilog(lvl=0,e="Decreasing profit window must be less than window end -1.")
|
# state.ilog(lvl=0,e="Decreasing profit window must be less than window end -1.")
|
||||||
@ -74,7 +72,7 @@ def eod_exit_activated(state: StrategyState, activeTrade: Trade, data, direction
|
|||||||
state.ilog(lvl=1,e=f"Forced Exit Window OPEN - breakeven check", msg=f"{forced_exit_window_start=} {forced_exit_window_end=} ", time=str(datetime.fromtimestamp(data['updated']).astimezone(zoneNY)))
|
state.ilog(lvl=1,e=f"Forced Exit Window OPEN - breakeven check", msg=f"{forced_exit_window_start=} {forced_exit_window_end=} ", time=str(datetime.fromtimestamp(data['updated']).astimezone(zoneNY)))
|
||||||
|
|
||||||
directive_name = "forced_exit_breakeven_period"
|
directive_name = "forced_exit_breakeven_period"
|
||||||
forced_exit_breakeven_period = get_signal_section_directive(state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, True))
|
forced_exit_breakeven_period = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, True))
|
||||||
|
|
||||||
if forced_exit_breakeven_period is False:
|
if forced_exit_breakeven_period is False:
|
||||||
return False
|
return False
|
||||||
@ -82,11 +80,11 @@ def eod_exit_activated(state: StrategyState, activeTrade: Trade, data, direction
|
|||||||
#zatim krom posledni minuty cekame alespon na breakeven
|
#zatim krom posledni minuty cekame alespon na breakeven
|
||||||
curr_price = float(data['close'])
|
curr_price = float(data['close'])
|
||||||
#short smer
|
#short smer
|
||||||
if direction == TradeDirection.SHORT and curr_price<=float(avgp):
|
if direction == TradeDirection.SHORT and curr_price<=float(state.avgp):
|
||||||
state.ilog(lvl=1,e=f"Forced Exit - price better than avgp, dir SHORT")
|
state.ilog(lvl=1,e=f"Forced Exit - price better than avgp, dir SHORT")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
if direction == TradeDirection.LONG and curr_price>=float(avgp):
|
if direction == TradeDirection.LONG and curr_price>=float(state.avgp):
|
||||||
state.ilog(lvl=1,e=f"Forced Exit - price better than avgp, dir LONG")
|
state.ilog(lvl=1,e=f"Forced Exit - price better than avgp, dir LONG")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|||||||
@ -1,207 +1,198 @@
|
|||||||
from v2realbot.strategyblocks.activetrade.close.close_position import close_position, close_position_partial
|
from v2realbot.strategyblocks.activetrade.close.close_position import close_position, close_position_partial
|
||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
from v2realbot.enums.enums import Followup
|
from v2realbot.enums.enums import Followup
|
||||||
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
|
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
||||||
from v2realbot.utils.utils import safe_get
|
from v2realbot.utils.utils import safe_get
|
||||||
from v2realbot.config import KW
|
from v2realbot.config import KW
|
||||||
#from icecream import install, ic
|
#from icecream import install, ic
|
||||||
from rich import print as printanyway
|
from rich import print as printanyway
|
||||||
from threading import Event
|
from threading import Event
|
||||||
#import gaka
|
|
||||||
from v2realbot.utils.utils import gaka
|
|
||||||
import os
|
import os
|
||||||
from traceback import format_exc
|
from traceback import format_exc
|
||||||
from v2realbot.strategyblocks.activetrade.close.eod_exit import eod_exit_activated
|
from v2realbot.strategyblocks.activetrade.close.eod_exit import eod_exit_activated
|
||||||
from v2realbot.strategyblocks.activetrade.close.conditions import dontexit_protection_met, exit_conditions_met
|
from v2realbot.strategyblocks.activetrade.close.conditions import dontexit_protection_met, exit_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.strategyblocks.activetrade.helpers import get_max_profit_price, get_profit_target_price, get_override_for_active_trade, keyword_conditions_met
|
||||||
from v2realbot.strategyblocks.activetrade.sl.optimsl import SLOptimizer
|
from v2realbot.strategyblocks.activetrade.sl.optimsl import SLOptimizer
|
||||||
|
|
||||||
#TODO tady odsud
|
def eval_close_position(state: StrategyState, data):
|
||||||
def eval_close_position(state: StrategyState, accountsWithActiveTrade, data):
|
|
||||||
|
|
||||||
curr_price = float(data['close'])
|
curr_price = float(data['close'])
|
||||||
state.ilog(lvl=0,e="Eval CLOSE", price=curr_price, pos=gaka(state.account_variables, "positions"), avgp=gaka(state.account_variables, "avgp"), pending=gaka(state.account_variables, "pending"), activeTrade=str(gaka(state.account_variables, "activeTrade")))
|
state.ilog(lvl=0,e="Eval CLOSE", price=curr_price, pos=state.positions, avgp=state.avgp, pending=state.vars.pending, activeTrade=str(state.vars.activeTrade))
|
||||||
|
|
||||||
#iterate over accountsWithActiveTrade
|
if int(state.positions) != 0 and float(state.avgp)>0 and state.vars.pending is None:
|
||||||
for account_str, activeTrade in accountsWithActiveTrade.items():
|
|
||||||
positions = state.account_variables[account_str].positions
|
#close position handling
|
||||||
avgp = state.account_variables[account_str].avgp
|
#TBD pridat OPTIMALIZACI POZICE - EXIT 1/2
|
||||||
pending = state.account_variables[account_str].pending
|
|
||||||
if int(positions) != 0 and float(avgp)>0 and pending is None:
|
#mame short pozice - (IDEA: rozlisovat na zaklade aktivniho tradu - umozni mi spoustet i pri soucasne long pozicemi)
|
||||||
|
if int(state.positions) < 0:
|
||||||
|
#get TARGET PRICE pro dany smer a signal
|
||||||
|
|
||||||
|
#pokud existujeme bereme z nastaveni tradu a nebo z defaultu
|
||||||
|
if state.vars.activeTrade.goal_price is not None:
|
||||||
|
goal_price = state.vars.activeTrade.goal_price
|
||||||
|
else:
|
||||||
|
goal_price = get_profit_target_price(state, data, TradeDirection.SHORT)
|
||||||
|
|
||||||
|
max_price = get_max_profit_price(state, data, TradeDirection.SHORT)
|
||||||
|
state.ilog(lvl=1,e=f"Def Goal price {str(TradeDirection.SHORT)} {goal_price} max price {max_price}")
|
||||||
|
|
||||||
#close position handling
|
#SL OPTIMALIZATION - PARTIAL EXIT
|
||||||
#TBD pridat OPTIMALIZACI POZICE - EXIT 1/2
|
level_met, exit_adjustment = state.sl_optimizer_short.eval_position(state, data)
|
||||||
|
if level_met is not None and exit_adjustment is not None:
|
||||||
|
position = state.positions * exit_adjustment
|
||||||
|
state.ilog(lvl=1,e=f"SL OPTIMIZATION ENGAGED {str(TradeDirection.SHORT)} {position=} {level_met=} {exit_adjustment}", initial_levels=str(state.sl_optimizer_short.get_initial_abs_levels(state)), rem_levels=str(state.sl_optimizer_short.get_remaining_abs_levels(state)), exit_levels=str(state.sl_optimizer_short.exit_levels), exit_sizes=str(state.sl_optimizer_short.exit_sizes))
|
||||||
|
printanyway(f"SL OPTIMIZATION ENGAGED {str(TradeDirection.SHORT)} {position=} {level_met=} {exit_adjustment}")
|
||||||
|
close_position_partial(state=state, data=data, direction=TradeDirection.SHORT, reason=F"SL OPT LEVEL {level_met} REACHED", size=exit_adjustment)
|
||||||
|
return
|
||||||
|
|
||||||
|
#FULL SL reached - execution
|
||||||
|
if curr_price > state.vars.activeTrade.stoploss_value:
|
||||||
|
|
||||||
#mame short pozice - (IDEA: rozlisovat na zaklade aktivniho tradu - umozni mi spoustet i pri soucasne long pozicemi)
|
directive_name = 'reverse_for_SL_exit_short'
|
||||||
if int(positions) < 0:
|
reverse_for_SL_exit = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, "no"))
|
||||||
#get TARGET PRICE pro dany smer a signal
|
|
||||||
|
|
||||||
#pokud existujeme bereme z nastaveni tradu a nebo z defaultu
|
if reverse_for_SL_exit == "always":
|
||||||
if activeTrade.goal_price is not None:
|
followup_action = Followup.REVERSE
|
||||||
goal_price = activeTrade.goal_price
|
elif reverse_for_SL_exit == "cond":
|
||||||
|
followup_action = Followup.REVERSE if keyword_conditions_met(state, data, direction=TradeDirection.SHORT, keyword=KW.slreverseonly, skip_conf_validation=True) else None
|
||||||
else:
|
else:
|
||||||
goal_price = get_profit_target_price(state, data, activeTrade, TradeDirection.SHORT)
|
followup_action = None
|
||||||
|
close_position(state=state, data=data, direction=TradeDirection.SHORT, reason="SL REACHED", followup=followup_action)
|
||||||
max_price = get_max_profit_price(state, activeTrade, data, TradeDirection.SHORT)
|
return
|
||||||
state.ilog(lvl=1,e=f"Def Goal price {str(TradeDirection.SHORT)} {goal_price} max price {max_price}")
|
|
||||||
|
|
||||||
#SL OPTIMALIZATION - PARTIAL EXIT
|
|
||||||
level_met, exit_adjustment = state.sl_optimizer_short.eval_position(state, data, activeTrade)
|
#REVERSE BASED ON REVERSE CONDITIONS
|
||||||
if level_met is not None and exit_adjustment is not None:
|
if keyword_conditions_met(state, data, direction=TradeDirection.SHORT, keyword=KW.reverse):
|
||||||
position = positions * exit_adjustment
|
close_position(state=state, data=data, direction=TradeDirection.SHORT, reason="REVERSE COND MET", followup=Followup.REVERSE)
|
||||||
state.ilog(lvl=1,e=f"SL OPTIMIZATION ENGAGED {str(TradeDirection.SHORT)} {position=} {level_met=} {exit_adjustment}", initial_levels=str(state.sl_optimizer_short.get_initial_abs_levels(state, activeTrade)), rem_levels=str(state.sl_optimizer_short.get_remaining_abs_levels(state, activeTrade)), exit_levels=str(state.sl_optimizer_short.exit_levels), exit_sizes=str(state.sl_optimizer_short.exit_sizes))
|
return
|
||||||
printanyway(f"SL OPTIMIZATION ENGAGED {str(TradeDirection.SHORT)} {position=} {level_met=} {exit_adjustment}")
|
|
||||||
close_position_partial(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.SHORT, reason=F"SL OPT LEVEL {level_met} REACHED", size=exit_adjustment)
|
|
||||||
return
|
|
||||||
|
|
||||||
#FULL SL reached - execution
|
|
||||||
if curr_price > activeTrade.stoploss_value:
|
|
||||||
|
|
||||||
directive_name = 'reverse_for_SL_exit_short'
|
#EXIT ADD CONDITIONS MET (exit and add)
|
||||||
reverse_for_SL_exit = get_signal_section_directive(state=state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, "no"))
|
if keyword_conditions_met(state, data, direction=TradeDirection.SHORT, keyword=KW.exitadd):
|
||||||
|
close_position(state=state, data=data, direction=TradeDirection.SHORT, reason="EXITADD COND MET", followup=Followup.ADD)
|
||||||
|
return
|
||||||
|
|
||||||
if reverse_for_SL_exit == "always":
|
#CLOSING BASED ON EXIT CONDITIONS
|
||||||
|
if exit_conditions_met(state, data, TradeDirection.SHORT):
|
||||||
|
directive_name = 'reverse_for_cond_exit_short'
|
||||||
|
reverse_for_cond_exit_short = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
||||||
|
directive_name = 'add_for_cond_exit_short'
|
||||||
|
add_for_cond_exit_short = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
||||||
|
if reverse_for_cond_exit_short:
|
||||||
followup_action = Followup.REVERSE
|
followup_action = Followup.REVERSE
|
||||||
elif reverse_for_SL_exit == "cond":
|
elif add_for_cond_exit_short:
|
||||||
followup_action = Followup.REVERSE if keyword_conditions_met(state, data=data, activeTrade=activeTrade, direction=TradeDirection.SHORT, keyword=KW.slreverseonly, skip_conf_validation=True) else None
|
followup_action = Followup.ADD
|
||||||
else:
|
else:
|
||||||
followup_action = None
|
followup_action = None
|
||||||
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.SHORT, reason="SL REACHED", followup=followup_action)
|
close_position(state=state, data=data, direction=TradeDirection.SHORT, reason="EXIT COND MET", followup=followup_action)
|
||||||
|
return
|
||||||
|
|
||||||
|
#PROFIT
|
||||||
|
if curr_price<=goal_price:
|
||||||
|
#TODO cekat az slope prestane intenzivn erust, necekat az na klesani
|
||||||
|
#TODO mozna cekat na nejaky signal RSI
|
||||||
|
#TODO pripadne pokud dosahne TGTBB prodat ihned
|
||||||
|
max_price_signal = curr_price<=max_price
|
||||||
|
#OPTIMALIZACE pri stoupajícím angle
|
||||||
|
if max_price_signal or dontexit_protection_met(state=state, data=data,direction=TradeDirection.SHORT) is False:
|
||||||
|
close_position(state=state, data=data, direction=TradeDirection.SHORT, reason=f"PROFIT or MAXPROFIT REACHED {max_price_signal=}")
|
||||||
return
|
return
|
||||||
|
#pokud je cena horsi, ale byla uz dont exit aktivovany - pak prodavame také
|
||||||
|
elif state.dont_exit_already_activated == True:
|
||||||
#REVERSE BASED ON REVERSE CONDITIONS
|
#TODO toto mozna take na direktivu, timto neprodavame pokud porkacuje trend - EXIT_PROT_BOUNCE_IMMEDIATE
|
||||||
if keyword_conditions_met(state, data, activeTrade=activeTrade, direction=TradeDirection.SHORT, keyword=KW.reverse):
|
#if dontexit_protection_met(state=state, data=data,direction=TradeDirection.SHORT) is False:
|
||||||
close_position(state=state, activeTrade=activeTrade,data=data, direction=TradeDirection.SHORT, reason="REVERSE COND MET", followup=Followup.REVERSE)
|
close_position(state=state, data=data, direction=TradeDirection.SHORT, reason=f"EXIT PROTECTION BOUNCE {state.dont_exit_already_activated=}")
|
||||||
return
|
state.dont_exit_already_activated = False
|
||||||
|
return
|
||||||
|
|
||||||
#EXIT ADD CONDITIONS MET (exit and add)
|
#FORCED EXIT PRI KONCI DNE
|
||||||
if keyword_conditions_met(state, data, activeTrade=activeTrade, direction=TradeDirection.SHORT, keyword=KW.exitadd):
|
if eod_exit_activated(state, data, TradeDirection.SHORT):
|
||||||
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.SHORT, reason="EXITADD COND MET", followup=Followup.ADD)
|
close_position(state=state, data=data, direction=TradeDirection.SHORT, reason="EOD EXIT ACTIVATED")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
#mame long
|
||||||
|
elif int(state.positions) > 0:
|
||||||
|
|
||||||
#CLOSING BASED ON EXIT CONDITIONS
|
#get TARGET PRICE pro dany smer a signal
|
||||||
if exit_conditions_met(state, activeTrade, data, TradeDirection.SHORT):
|
#pokud existujeme bereme z nastaveni tradu a nebo z defaultu
|
||||||
directive_name = 'reverse_for_cond_exit_short'
|
if state.vars.activeTrade.goal_price is not None:
|
||||||
reverse_for_cond_exit_short = get_signal_section_directive(state=state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
goal_price = state.vars.activeTrade.goal_price
|
||||||
directive_name = 'add_for_cond_exit_short'
|
else:
|
||||||
add_for_cond_exit_short = get_signal_section_directive(state=state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
goal_price = get_profit_target_price(state, data, TradeDirection.LONG)
|
||||||
if reverse_for_cond_exit_short:
|
|
||||||
followup_action = Followup.REVERSE
|
max_price = get_max_profit_price(state, data, TradeDirection.LONG)
|
||||||
elif add_for_cond_exit_short:
|
state.ilog(lvl=1,e=f"Goal price {str(TradeDirection.LONG)} {goal_price} max price {max_price}")
|
||||||
followup_action = Followup.ADD
|
|
||||||
else:
|
|
||||||
followup_action = None
|
|
||||||
close_position(state=state, activeTrae=activeTrade, data=data, direction=TradeDirection.SHORT, reason="EXIT COND MET", followup=followup_action)
|
|
||||||
return
|
|
||||||
|
|
||||||
#PROFIT
|
#SL OPTIMALIZATION - PARTIAL EXIT
|
||||||
if curr_price<=goal_price:
|
level_met, exit_adjustment = state.sl_optimizer_long.eval_position(state, data)
|
||||||
#TODO cekat az slope prestane intenzivn erust, necekat az na klesani
|
if level_met is not None and exit_adjustment is not None:
|
||||||
#TODO mozna cekat na nejaky signal RSI
|
position = state.positions * exit_adjustment
|
||||||
#TODO pripadne pokud dosahne TGTBB prodat ihned
|
state.ilog(lvl=1,e=f"SL OPTIMIZATION ENGAGED {str(TradeDirection.LONG)} {position=} {level_met=} {exit_adjustment}", initial_levels=str(state.sl_optimizer_long.get_initial_abs_levels(state)), rem_levels=str(state.sl_optimizer_long.get_remaining_abs_levels(state)), exit_levels=str(state.sl_optimizer_long.exit_levels), exit_sizes=str(state.sl_optimizer_long.exit_sizes))
|
||||||
max_price_signal = curr_price<=max_price
|
printanyway(f"SL OPTIMIZATION ENGAGED {str(TradeDirection.LONG)} {position=} {level_met=} {exit_adjustment}")
|
||||||
#OPTIMALIZACE pri stoupajícím angle
|
close_position_partial(state=state, data=data, direction=TradeDirection.LONG, reason=f"SL OPT LEVEL {level_met} REACHED", size=exit_adjustment)
|
||||||
if max_price_signal or dontexit_protection_met(state=state, activeTrade=activeTrade, data=data,direction=TradeDirection.SHORT) is False:
|
return
|
||||||
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.SHORT, reason=f"PROFIT or MAXPROFIT REACHED {max_price_signal=}")
|
|
||||||
return
|
|
||||||
#pokud je cena horsi, ale byla uz dont exit aktivovany - pak prodavame také
|
|
||||||
elif state.account_variables[activeTrade.account.name].dont_exit_already_activated == True:
|
|
||||||
#TODO toto mozna take na direktivu, timto neprodavame pokud porkacuje trend - EXIT_PROT_BOUNCE_IMMEDIATE
|
|
||||||
#if dontexit_protection_met(state=state, data=data,direction=TradeDirection.SHORT) is False:
|
|
||||||
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.SHORT, reason=f"EXIT PROTECTION BOUNCE {state.account_variables[activeTrade.account.name].dont_exit_already_activated=}")
|
|
||||||
state.account_variables[activeTrade.account.name].dont_exit_already_activated = False
|
|
||||||
return
|
|
||||||
|
|
||||||
#FORCED EXIT PRI KONCI DNE
|
#SL FULL execution
|
||||||
if eod_exit_activated(state, activeTrade=activeTrade, data=data, direction=TradeDirection.SHORT):
|
if curr_price < state.vars.activeTrade.stoploss_value:
|
||||||
close_position(state=state, activeTrade=activeTrade,data=data, direction=TradeDirection.SHORT, reason="EOD EXIT ACTIVATED")
|
directive_name = 'reverse_for_SL_exit_long'
|
||||||
return
|
reverse_for_SL_exit = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, "no"))
|
||||||
|
|
||||||
#mame long
|
|
||||||
elif int(positions) > 0:
|
|
||||||
|
|
||||||
#get TARGET PRICE pro dany smer a signal
|
state.ilog(lvl=1, e=f"reverse_for_SL_exit {reverse_for_SL_exit}")
|
||||||
#pokud existujeme bereme z nastaveni tradu a nebo z defaultu
|
|
||||||
if activeTrade.goal_price is not None:
|
if reverse_for_SL_exit == "always":
|
||||||
goal_price = activeTrade.goal_price
|
followup_action = Followup.REVERSE
|
||||||
|
elif reverse_for_SL_exit == "cond":
|
||||||
|
followup_action = Followup.REVERSE if keyword_conditions_met(state, data, direction=TradeDirection.LONG, keyword=KW.slreverseonly, skip_conf_validation=True) else None
|
||||||
else:
|
else:
|
||||||
goal_price = get_profit_target_price(state, data, activeTrade, TradeDirection.LONG)
|
followup_action = None
|
||||||
|
|
||||||
|
close_position(state=state, data=data, direction=TradeDirection.LONG, reason="SL REACHED", followup=followup_action)
|
||||||
|
return
|
||||||
|
|
||||||
max_price = get_max_profit_price(state, activeTrade, data, TradeDirection.LONG)
|
|
||||||
state.ilog(lvl=1,e=f"Goal price {str(TradeDirection.LONG)} {goal_price} max price {max_price}")
|
|
||||||
|
|
||||||
#SL OPTIMALIZATION - PARTIAL EXIT
|
#REVERSE BASED ON REVERSE CONDITIONS
|
||||||
level_met, exit_adjustment = state.sl_optimizer_long.eval_position(state, data, activeTrade)
|
if keyword_conditions_met(state, data,TradeDirection.LONG, KW.reverse):
|
||||||
if level_met is not None and exit_adjustment is not None:
|
close_position(state=state, data=data, direction=TradeDirection.LONG, reason="REVERSE COND MET", followup=Followup.REVERSE)
|
||||||
position = positions * exit_adjustment
|
return
|
||||||
state.ilog(lvl=1,e=f"SL OPTIMIZATION ENGAGED {str(TradeDirection.LONG)} {position=} {level_met=} {exit_adjustment}", initial_levels=str(state.sl_optimizer_long.get_initial_abs_levels(state, activeTrade)), rem_levels=str(state.sl_optimizer_long.get_remaining_abs_levels(state, activeTrade)), exit_levels=str(state.sl_optimizer_long.exit_levels), exit_sizes=str(state.sl_optimizer_long.exit_sizes))
|
|
||||||
printanyway(f"SL OPTIMIZATION ENGAGED {str(TradeDirection.LONG)} {position=} {level_met=} {exit_adjustment}")
|
|
||||||
close_position_partial(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.LONG, reason=f"SL OPT LEVEL {level_met} REACHED", size=exit_adjustment)
|
|
||||||
return
|
|
||||||
|
|
||||||
#SL FULL execution
|
#EXIT ADD CONDITIONS MET (exit and add)
|
||||||
if curr_price < activeTrade.stoploss_value:
|
if keyword_conditions_met(state, data, TradeDirection.LONG, KW.exitadd):
|
||||||
directive_name = 'reverse_for_SL_exit_long'
|
close_position(state=state, data=data, direction=TradeDirection.LONG, reason="EXITADD COND MET", followup=Followup.ADD)
|
||||||
reverse_for_SL_exit = get_signal_section_directive(state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, "no"))
|
return
|
||||||
|
|
||||||
state.ilog(lvl=1, e=f"reverse_for_SL_exit {reverse_for_SL_exit}")
|
#EXIT CONDITIONS
|
||||||
|
if exit_conditions_met(state, data, TradeDirection.LONG):
|
||||||
if reverse_for_SL_exit == "always":
|
directive_name = 'reverse_for_cond_exit_long'
|
||||||
|
reverse_for_cond_exit_long = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
||||||
|
directive_name = 'add_for_cond_exit_long'
|
||||||
|
add_for_cond_exit_long = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
||||||
|
if reverse_for_cond_exit_long:
|
||||||
followup_action = Followup.REVERSE
|
followup_action = Followup.REVERSE
|
||||||
elif reverse_for_SL_exit == "cond":
|
elif add_for_cond_exit_long:
|
||||||
followup_action = Followup.REVERSE if keyword_conditions_met(state, data, activeTrade, direction=TradeDirection.LONG, keyword=KW.slreverseonly, skip_conf_validation=True) else None
|
followup_action = Followup.ADD
|
||||||
else:
|
else:
|
||||||
followup_action = None
|
followup_action = None
|
||||||
|
close_position(state=state, data=data, direction=TradeDirection.LONG, reason="EXIT CONDS MET", followup=followup_action)
|
||||||
|
return
|
||||||
|
|
||||||
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.LONG, reason="SL REACHED", followup=followup_action)
|
#PROFIT
|
||||||
|
if curr_price>=goal_price:
|
||||||
|
#TODO cekat az slope prestane intenzivn erust, necekat az na klesani
|
||||||
|
#TODO mozna cekat na nejaky signal RSI
|
||||||
|
#TODO pripadne pokud dosahne TGTBB prodat ihned
|
||||||
|
max_price_signal = curr_price>=max_price
|
||||||
|
#OPTIMALIZACE pri stoupajícím angle
|
||||||
|
if max_price_signal or dontexit_protection_met(state, data, direction=TradeDirection.LONG) is False:
|
||||||
|
close_position(state=state, data=data, direction=TradeDirection.LONG, reason=f"PROFIT or MAXPROFIT REACHED {max_price_signal=}")
|
||||||
return
|
return
|
||||||
|
#pokud je cena horsi, ale byl uz dont exit aktivovany - pak prodavame také
|
||||||
|
elif state.dont_exit_already_activated == True:
|
||||||
#REVERSE BASED ON REVERSE CONDITIONS
|
#TODO toto mozna take na direktivu, timto neprodavame pokud porkacuje trend - EXIT_PROT_BOUNCE_IMMEDIATE
|
||||||
if keyword_conditions_met(state, data, activeTrade, TradeDirection.LONG, KW.reverse):
|
# if dontexit_protection_met(state=state, data=data,direction=TradeDirection.LONG) is False:
|
||||||
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.LONG, reason="REVERSE COND MET", followup=Followup.REVERSE)
|
close_position(state=state, data=data, direction=TradeDirection.LONG, reason=f"EXIT PROTECTION BOUNCE {state.dont_exit_already_activated=}")
|
||||||
return
|
state.dont_exit_already_activated = False
|
||||||
|
return
|
||||||
#EXIT ADD CONDITIONS MET (exit and add)
|
|
||||||
if keyword_conditions_met(state, data, activeTrade, TradeDirection.LONG, KW.exitadd):
|
#FORCED EXIT PRI KONCI DNE
|
||||||
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.LONG, reason="EXITADD COND MET", followup=Followup.ADD)
|
if eod_exit_activated(state, data, TradeDirection.LONG):
|
||||||
return
|
close_position(state=state, data=data, direction=TradeDirection.LONG, reason="EOD EXIT ACTIVATED")
|
||||||
|
return
|
||||||
#EXIT CONDITIONS
|
|
||||||
if exit_conditions_met(state, activeTrade, data, TradeDirection.LONG):
|
|
||||||
directive_name = 'reverse_for_cond_exit_long'
|
|
||||||
reverse_for_cond_exit_long = get_signal_section_directive(state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
|
||||||
directive_name = 'add_for_cond_exit_long'
|
|
||||||
add_for_cond_exit_long = get_signal_section_directive(state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
|
||||||
if reverse_for_cond_exit_long:
|
|
||||||
followup_action = Followup.REVERSE
|
|
||||||
elif add_for_cond_exit_long:
|
|
||||||
followup_action = Followup.ADD
|
|
||||||
else:
|
|
||||||
followup_action = None
|
|
||||||
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.LONG, reason="EXIT CONDS MET", followup=followup_action)
|
|
||||||
return
|
|
||||||
|
|
||||||
#PROFIT
|
|
||||||
if curr_price>=goal_price:
|
|
||||||
#TODO cekat az slope prestane intenzivn erust, necekat az na klesani
|
|
||||||
#TODO mozna cekat na nejaky signal RSI
|
|
||||||
#TODO pripadne pokud dosahne TGTBB prodat ihned
|
|
||||||
max_price_signal = curr_price>=max_price
|
|
||||||
#OPTIMALIZACE pri stoupajícím angle
|
|
||||||
if max_price_signal or dontexit_protection_met(state, activeTrade=activeTrade, data=data, direction=TradeDirection.LONG) is False:
|
|
||||||
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.LONG, reason=f"PROFIT or MAXPROFIT REACHED {max_price_signal=}")
|
|
||||||
return
|
|
||||||
#pokud je cena horsi, ale byl uz dont exit aktivovany - pak prodavame také
|
|
||||||
elif state.account_variables[activeTrade.account.name].dont_exit_already_activated == True:
|
|
||||||
#TODO toto mozna take na direktivu, timto neprodavame pokud porkacuje trend - EXIT_PROT_BOUNCE_IMMEDIATE
|
|
||||||
# if dontexit_protection_met(state=state, data=data,direction=TradeDirection.LONG) is False:
|
|
||||||
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.LONG, reason=f"EXIT PROTECTION BOUNCE {state.account_variables[activeTrade.account.name].dont_exit_already_activated=}")
|
|
||||||
state.account_variables[activeTrade.account.name].dont_exit_already_activated = False
|
|
||||||
return
|
|
||||||
|
|
||||||
#FORCED EXIT PRI KONCI DNE
|
|
||||||
if eod_exit_activated(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.LONG):
|
|
||||||
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.LONG, reason="EOD EXIT ACTIVATED")
|
|
||||||
return
|
|
||||||
@ -1,5 +1,5 @@
|
|||||||
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
|
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
|
||||||
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
|
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff, create_new_bars, slice_dict_lists
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff, create_new_bars, slice_dict_lists
|
||||||
from v2realbot.utils.directive_utils import get_conditions_from_configuration
|
from v2realbot.utils.directive_utils import get_conditions_from_configuration
|
||||||
from v2realbot.common.model import SLHistory
|
from v2realbot.common.model import SLHistory
|
||||||
@ -18,20 +18,17 @@ from traceback import format_exc
|
|||||||
from v2realbot.strategyblocks.helpers import normalize_tick
|
from v2realbot.strategyblocks.helpers import normalize_tick
|
||||||
from v2realbot.strategyblocks.indicators.helpers import evaluate_directive_conditions
|
from v2realbot.strategyblocks.indicators.helpers import evaluate_directive_conditions
|
||||||
|
|
||||||
#TODO zde dodelat viz nize get get_signal_section_directive a pak pokracovat v close positions
|
|
||||||
#otestuje keyword podminky (napr. reverse_if, nebo exitadd_if)
|
#otestuje keyword podminky (napr. reverse_if, nebo exitadd_if)
|
||||||
def keyword_conditions_met(state, data, activeTrade: Trade, direction: TradeDirection, keyword: KW, skip_conf_validation: bool = False):
|
def keyword_conditions_met(state, data, direction: TradeDirection, keyword: KW, skip_conf_validation: bool = False):
|
||||||
action = str(keyword).upper()
|
action = str(keyword).upper()
|
||||||
if direction == TradeDirection.LONG:
|
if direction == TradeDirection.LONG:
|
||||||
smer = "long"
|
smer = "long"
|
||||||
else:
|
else:
|
||||||
smer = "short"
|
smer = "short"
|
||||||
|
|
||||||
mother_signal = activeTrade.generated_by
|
|
||||||
|
|
||||||
if skip_conf_validation is False:
|
if skip_conf_validation is False:
|
||||||
directive_name = "exit_cond_only_on_confirmed"
|
directive_name = "exit_cond_only_on_confirmed"
|
||||||
exit_cond_only_on_confirmed = get_signal_section_directive(state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
exit_cond_only_on_confirmed = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
|
||||||
|
|
||||||
if exit_cond_only_on_confirmed and data['confirmed'] == 0:
|
if exit_cond_only_on_confirmed and data['confirmed'] == 0:
|
||||||
state.ilog(lvl=0,e=f"{action} CHECK COND ONLY ON CONFIRMED BAR")
|
state.ilog(lvl=0,e=f"{action} CHECK COND ONLY ON CONFIRMED BAR")
|
||||||
@ -40,7 +37,7 @@ def keyword_conditions_met(state, data, activeTrade: Trade, direction: TradeDire
|
|||||||
#TOTO zatim u REVERSU neresime
|
#TOTO zatim u REVERSU neresime
|
||||||
# #POKUD je nastaven MIN PROFIT, zkontrolujeme ho a az pripadne pustime CONDITIONY
|
# #POKUD je nastaven MIN PROFIT, zkontrolujeme ho a az pripadne pustime CONDITIONY
|
||||||
# directive_name = "exit_cond_min_profit"
|
# directive_name = "exit_cond_min_profit"
|
||||||
# exit_cond_min_profit = get_signal_section_directive(directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
# exit_cond_min_profit = get_override_for_active_trade(directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
||||||
|
|
||||||
# #máme nastavený exit_cond_min_profit
|
# #máme nastavený exit_cond_min_profit
|
||||||
# # zjistíme, zda jsme v daném profit a případně nepustíme dál
|
# # zjistíme, zda jsme v daném profit a případně nepustíme dál
|
||||||
@ -80,6 +77,8 @@ def keyword_conditions_met(state, data, activeTrade: Trade, direction: TradeDire
|
|||||||
#bereme bud exit condition signalu, ktery activeTrade vygeneroval+ fallback na general
|
#bereme bud exit condition signalu, ktery activeTrade vygeneroval+ fallback na general
|
||||||
state.ilog(lvl=0,e=f"{action} CONDITIONS ENTRY {smer}", conditions=state.vars.conditions[KW.reverse])
|
state.ilog(lvl=0,e=f"{action} CONDITIONS ENTRY {smer}", conditions=state.vars.conditions[KW.reverse])
|
||||||
|
|
||||||
|
mother_signal = state.vars.activeTrade.generated_by
|
||||||
|
|
||||||
if mother_signal is not None:
|
if mother_signal is not None:
|
||||||
cond_dict = state.vars.conditions[keyword][mother_signal][smer]
|
cond_dict = state.vars.conditions[keyword][mother_signal][smer]
|
||||||
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "OR")
|
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "OR")
|
||||||
@ -109,12 +108,12 @@ def keyword_conditions_met(state, data, activeTrade: Trade, direction: TradeDire
|
|||||||
|
|
||||||
|
|
||||||
#mozna do SL helpers tuto
|
#mozna do SL helpers tuto
|
||||||
def insert_SL_history(state, activeTrade: Trade):
|
def insert_SL_history(state):
|
||||||
#insert stoploss history as key sl_history into runner archive extended data
|
#insert stoploss history as key sl_history into runner archive extended data
|
||||||
state.extData["sl_history"].append(SLHistory(id=activeTrade.id, time=state.time, sl_val=activeTrade.stoploss_value, direction=activeTrade.direction, account=activeTrade.account))
|
state.extData["sl_history"].append(SLHistory(id=state.vars.activeTrade.id, time=state.time, sl_val=state.vars.activeTrade.stoploss_value))
|
||||||
|
|
||||||
|
|
||||||
def get_default_sl_value(state, signal_name, direction: TradeDirection):
|
def get_default_sl_value(state, direction: TradeDirection):
|
||||||
|
|
||||||
if direction == TradeDirection.LONG:
|
if direction == TradeDirection.LONG:
|
||||||
smer = "long"
|
smer = "long"
|
||||||
@ -129,16 +128,15 @@ def get_default_sl_value(state, signal_name, direction: TradeDirection):
|
|||||||
state.ilog(lvl=1,e="No options for exit in stratvars. Fallback.")
|
state.ilog(lvl=1,e="No options for exit in stratvars. Fallback.")
|
||||||
return 0.01
|
return 0.01
|
||||||
directive_name = 'SL_defval_'+str(smer)
|
directive_name = 'SL_defval_'+str(smer)
|
||||||
val = get_signal_section_directive(state, signal_name, directive_name=directive_name, default_value=safe_get(options, directive_name, 0.01))
|
val = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(options, directive_name, 0.01))
|
||||||
return val
|
return val
|
||||||
#funkce pro direktivy, ktere muzou byt overridnute v signal sekci
|
#funkce pro direktivy, ktere muzou byt overridnute v signal sekci
|
||||||
#tato funkce vyhleda signal sekci aktivniho tradu a pokusi se danou direktivu vyhledat tam,
|
#tato funkce vyhleda signal sekci aktivniho tradu a pokusi se danou direktivu vyhledat tam,
|
||||||
#pokud nenajde tak vrati default, ktery byl poskytnut
|
#pokud nenajde tak vrati default, ktery byl poskytnut
|
||||||
#TODO toto predelat na jiny nazev get_overide_for_directive_section (vstup muze byt opuze signal_name)
|
def get_override_for_active_trade(state, directive_name: str, default_value: str):
|
||||||
def get_signal_section_directive(state, signal_name: str, directive_name: str, default_value: str):
|
|
||||||
val = default_value
|
val = default_value
|
||||||
override = "NO"
|
override = "NO"
|
||||||
mother_signal = signal_name
|
mother_signal = state.vars.activeTrade.generated_by
|
||||||
|
|
||||||
if mother_signal is not None:
|
if mother_signal is not None:
|
||||||
override = "YES "+mother_signal
|
override = "YES "+mother_signal
|
||||||
@ -147,30 +145,30 @@ def get_signal_section_directive(state, signal_name: str, directive_name: str, d
|
|||||||
state.ilog(lvl=0,e=f"{directive_name} OVERRIDE {override} NEWVAL:{val} ORIGINAL:{default_value} {mother_signal}", mother_signal=mother_signal,default_value=default_value)
|
state.ilog(lvl=0,e=f"{directive_name} OVERRIDE {override} NEWVAL:{val} ORIGINAL:{default_value} {mother_signal}", mother_signal=mother_signal,default_value=default_value)
|
||||||
return val
|
return val
|
||||||
|
|
||||||
def get_profit_target_price(state, data, activeTrade, direction: TradeDirection):
|
def get_profit_target_price(state, data, direction: TradeDirection):
|
||||||
if direction == TradeDirection.LONG:
|
if direction == TradeDirection.LONG:
|
||||||
smer = "long"
|
smer = "long"
|
||||||
else:
|
else:
|
||||||
smer = "short"
|
smer = "short"
|
||||||
|
|
||||||
directive_name = "profit"
|
directive_name = "profit"
|
||||||
def_profit_both_directions = get_signal_section_directive(state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, 0.50))
|
def_profit_both_directions = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, 0.50))
|
||||||
|
|
||||||
#profit pro dany smer
|
#profit pro dany smer
|
||||||
directive_name = 'profit_'+str(smer)
|
directive_name = 'profit_'+str(smer)
|
||||||
def_profit = get_signal_section_directive(state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=def_profit_both_directions)
|
def_profit = get_override_for_active_trade(state, directive_name=directive_name, default_value=def_profit_both_directions)
|
||||||
|
|
||||||
#mame v direktivve ticky
|
#mame v direktivve ticky
|
||||||
if isinstance(def_profit, (float, int)):
|
if isinstance(def_profit, (float, int)):
|
||||||
to_return = get_normalized_profitprice_from_tick(state, data, def_profit, activeTrade.account, direction)
|
to_return = get_normalized_profitprice_from_tick(state, data, def_profit, direction)
|
||||||
#mame v direktive indikator
|
#mame v direktive indikator
|
||||||
elif isinstance(def_profit, str):
|
elif isinstance(def_profit, str):
|
||||||
to_return = float(value_or_indicator(state, def_profit))
|
to_return = float(value_or_indicator(state, def_profit))
|
||||||
|
|
||||||
#min profit (ochrana extremnich hodnot indikatoru)
|
#min profit (ochrana extremnich hodnot indikatoru)
|
||||||
directive_name = 'profit_min_ind_tick_value'
|
directive_name = 'profit_min_ind_tick_value'
|
||||||
profit_min_ind_tick_value = get_signal_section_directive(state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=def_profit_both_directions)
|
profit_min_ind_tick_value = get_override_for_active_trade(state, directive_name=directive_name, default_value=def_profit_both_directions)
|
||||||
profit_min_ind_price_value = get_normalized_profitprice_from_tick(state, data, profit_min_ind_tick_value, activeTrade.account, direction)
|
profit_min_ind_price_value = get_normalized_profitprice_from_tick(state, data, profit_min_ind_tick_value, direction)
|
||||||
|
|
||||||
#ochrana pri nastaveni profitu prilis nizko
|
#ochrana pri nastaveni profitu prilis nizko
|
||||||
if direction == TradeDirection.LONG and to_return < profit_min_ind_price_value or direction == TradeDirection.SHORT and to_return > profit_min_ind_price_value:
|
if direction == TradeDirection.LONG and to_return < profit_min_ind_price_value or direction == TradeDirection.SHORT and to_return > profit_min_ind_price_value:
|
||||||
@ -181,32 +179,28 @@ def get_profit_target_price(state, data, activeTrade, direction: TradeDirection)
|
|||||||
return to_return
|
return to_return
|
||||||
|
|
||||||
##based on tick a direction, returns normalized prfoit price (LONG = avgp(nebo currprice)+norm.tick, SHORT=avgp(or currprice)-norm.tick)
|
##based on tick a direction, returns normalized prfoit price (LONG = avgp(nebo currprice)+norm.tick, SHORT=avgp(or currprice)-norm.tick)
|
||||||
def get_normalized_profitprice_from_tick(state, data, tick, account: Account, direction: TradeDirection):
|
def get_normalized_profitprice_from_tick(state, data, tick, direction: TradeDirection):
|
||||||
avgp = state.account_variables[account.name].avgp
|
|
||||||
normalized_tick = normalize_tick(state, data, float(tick))
|
normalized_tick = normalize_tick(state, data, float(tick))
|
||||||
base_price = avgp if avgp != 0 else data["close"]
|
base_price = state.avgp if state.avgp != 0 else data["close"]
|
||||||
returned_price = price2dec(float(base_price)+normalized_tick,3) if direction == TradeDirection.LONG else price2dec(float(base_price)-normalized_tick,3)
|
returned_price = price2dec(float(base_price)+normalized_tick,3) if direction == TradeDirection.LONG else price2dec(float(base_price)-normalized_tick,3)
|
||||||
state.ilog(lvl=0,e=f"NORMALIZED TICK {tick=} {normalized_tick=} NORM.PRICE {returned_price}")
|
state.ilog(lvl=0,e=f"NORMALIZED TICK {tick=} {normalized_tick=} NORM.PRICE {returned_price}")
|
||||||
return returned_price
|
return returned_price
|
||||||
|
|
||||||
def get_max_profit_price(state, activeTrade: Trade, data, direction: TradeDirection):
|
def get_max_profit_price(state, data, direction: TradeDirection):
|
||||||
if direction == TradeDirection.LONG:
|
if direction == TradeDirection.LONG:
|
||||||
smer = "long"
|
smer = "long"
|
||||||
else:
|
else:
|
||||||
smer = "short"
|
smer = "short"
|
||||||
|
|
||||||
directive_name = "max_profit"
|
directive_name = "max_profit"
|
||||||
max_profit_both_directions = get_signal_section_directive(state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, 0.35))
|
max_profit_both_directions = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, 0.35))
|
||||||
|
|
||||||
avgp = state.account_variables[activeTrade.account.name].avgp
|
|
||||||
positions = state.account_variables[activeTrade.account.name].positions
|
|
||||||
|
|
||||||
#max profit pro dany smer, s fallbackem na bez smeru
|
#max profit pro dany smer, s fallbackem na bez smeru
|
||||||
directive_name = 'max_profit_'+str(smer)
|
directive_name = 'max_profit_'+str(smer)
|
||||||
max_profit = get_signal_section_directive(state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=max_profit_both_directions)
|
max_profit = get_override_for_active_trade(state, directive_name=directive_name, default_value=max_profit_both_directions)
|
||||||
|
|
||||||
normalized_max_profit = normalize_tick(state,data,float(max_profit))
|
normalized_max_profit = normalize_tick(state,data,float(max_profit))
|
||||||
|
|
||||||
state.ilog(lvl=0,e=f"MAX PROFIT {max_profit=} {normalized_max_profit=}")
|
state.ilog(lvl=0,e=f"MAX PROFIT {max_profit=} {normalized_max_profit=}")
|
||||||
|
|
||||||
return price2dec(float(avgp)+normalized_max_profit,3) if int(positions) > 0 else price2dec(float(avgp)-normalized_max_profit,3)
|
return price2dec(float(state.avgp)+normalized_max_profit,3) if int(state.positions) > 0 else price2dec(float(state.avgp)-normalized_max_profit,3)
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
|
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
||||||
from typing import Tuple
|
from typing import Tuple
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
from v2realbot.strategyblocks.activetrade.helpers import get_signal_section_directive
|
from v2realbot.strategyblocks.activetrade.helpers import get_override_for_active_trade
|
||||||
from v2realbot.utils.utils import safe_get
|
from v2realbot.utils.utils import safe_get
|
||||||
# FIBONACCI PRO PROFIT A SL
|
# FIBONACCI PRO PROFIT A SL
|
||||||
|
|
||||||
@ -49,23 +49,23 @@ class SLOptimizer:
|
|||||||
# self.exit_levels = self.init_exit_levels
|
# self.exit_levels = self.init_exit_levels
|
||||||
# self.exit_sizes = self.init_exit_sizes
|
# self.exit_sizes = self.init_exit_sizes
|
||||||
|
|
||||||
def get_trade_details(self, state, activeTrade):
|
def get_trade_details(self, state):
|
||||||
trade: Trade = activeTrade
|
trade: Trade = state.vars.activeTrade
|
||||||
#jde o novy trade - resetujeme levely
|
#jde o novy trade - resetujeme levely
|
||||||
if trade.id != self.last_trade:
|
if trade.id != self.last_trade:
|
||||||
#inicializujeme a vymazeme pripadne puvodni
|
#inicializujeme a vymazeme pripadne puvodni
|
||||||
if self.initialize_levels(state, activeTrade) is False:
|
if self.initialize_levels(state) is False:
|
||||||
return None, None
|
return None, None
|
||||||
self.last_trade = trade.id
|
self.last_trade = trade.id
|
||||||
#return cost_price, sl_price
|
#return cost_price, sl_price
|
||||||
return state.account_variables[trade.account.name].avgp, trade.stoploss_value
|
return state.avgp, trade.stoploss_value
|
||||||
|
|
||||||
def initialize_levels(self, state, activeTrade):
|
def initialize_levels(self, state):
|
||||||
directive_name = 'SL_opt_exit_levels_'+str(self.direction.value)
|
directive_name = 'SL_opt_exit_levels_'+str(self.direction.value)
|
||||||
SL_opt_exit_levels = get_signal_section_directive(state=state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
SL_opt_exit_levels = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
||||||
|
|
||||||
directive_name = 'SL_opt_exit_sizes_'+str(self.direction.value)
|
directive_name = 'SL_opt_exit_sizes_'+str(self.direction.value)
|
||||||
SL_opt_exit_sizes = get_signal_section_directive(state=state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
|
SL_opt_exit_sizes = get_override_for_active_trade(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 None:
|
if SL_opt_exit_levels is None or SL_opt_exit_sizes is None:
|
||||||
#print("no directives found: SL_opt_exit_levels/SL_opt_exit_sizes")
|
#print("no directives found: SL_opt_exit_levels/SL_opt_exit_sizes")
|
||||||
@ -83,11 +83,11 @@ class SLOptimizer:
|
|||||||
print(f"new levels initialized {self.exit_levels=} {self.exit_sizes=}")
|
print(f"new levels initialized {self.exit_levels=} {self.exit_sizes=}")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_initial_abs_levels(self, state, activeTrade):
|
def get_initial_abs_levels(self, state):
|
||||||
"""
|
"""
|
||||||
Returns price levels corresponding to initial setting of exit_levels
|
Returns price levels corresponding to initial setting of exit_levels
|
||||||
"""
|
"""
|
||||||
cost_price, sl_price = self.get_trade_details(state, activeTrade)
|
cost_price, sl_price = self.get_trade_details(state)
|
||||||
if cost_price is None or sl_price is None:
|
if cost_price is None or sl_price is None:
|
||||||
return []
|
return []
|
||||||
curr_sl_distance = np.abs(cost_price - sl_price)
|
curr_sl_distance = np.abs(cost_price - sl_price)
|
||||||
@ -96,11 +96,11 @@ class SLOptimizer:
|
|||||||
else:
|
else:
|
||||||
return [cost_price - exit_level * curr_sl_distance for exit_level in self.init_exit_levels]
|
return [cost_price - exit_level * curr_sl_distance for exit_level in self.init_exit_levels]
|
||||||
|
|
||||||
def get_remaining_abs_levels(self, state, activeTrade):
|
def get_remaining_abs_levels(self, state):
|
||||||
"""
|
"""
|
||||||
Returns price levels corresponding to remaing exit_levels for current trade
|
Returns price levels corresponding to remaing exit_levels for current trade
|
||||||
"""
|
"""
|
||||||
cost_price, sl_price = self.get_trade_details(state, activeTrade)
|
cost_price, sl_price = self.get_trade_details(state)
|
||||||
if cost_price is None or sl_price is None:
|
if cost_price is None or sl_price is None:
|
||||||
return []
|
return []
|
||||||
curr_sl_distance = np.abs(cost_price - sl_price)
|
curr_sl_distance = np.abs(cost_price - sl_price)
|
||||||
@ -109,11 +109,11 @@ class SLOptimizer:
|
|||||||
else:
|
else:
|
||||||
return [cost_price - exit_level * curr_sl_distance for exit_level in self.exit_levels]
|
return [cost_price - exit_level * curr_sl_distance for exit_level in self.exit_levels]
|
||||||
|
|
||||||
def eval_position(self, state, data, activeTrade) -> Tuple[float, float]:
|
def eval_position(self, state, data) -> Tuple[float, float]:
|
||||||
"""Evaluates optimalization for current position and returns if the given level was
|
"""Evaluates optimalization for current position and returns if the given level was
|
||||||
met and how to adjust exit position.
|
met and how to adjust exit position.
|
||||||
"""
|
"""
|
||||||
cost_price, sl_price = self.get_trade_details(state, activeTrade)
|
cost_price, sl_price = self.get_trade_details(state)
|
||||||
if cost_price is None or sl_price is None:
|
if cost_price is None or sl_price is None:
|
||||||
#print("no settings found")
|
#print("no settings found")
|
||||||
return (None, None)
|
return (None, None)
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
|
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
||||||
from v2realbot.utils.utils import gaka, isrising, isfalling,zoneNY, price2dec, print, safe_get
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get
|
||||||
#from icecream import install, ic
|
#from icecream import install, ic
|
||||||
from rich import print as printanyway
|
from rich import print as printanyway
|
||||||
from threading import Event
|
from threading import Event
|
||||||
import os
|
import os
|
||||||
from traceback import format_exc
|
from traceback import format_exc
|
||||||
from v2realbot.strategyblocks.activetrade.helpers import get_signal_section_directive, normalize_tick, insert_SL_history
|
from v2realbot.strategyblocks.activetrade.helpers import get_override_for_active_trade, normalize_tick, insert_SL_history
|
||||||
|
|
||||||
|
|
||||||
#pokud se cena posouva nasim smerem olespon o (0.05) nad (SL + 0.09val), posuneme SL o offset
|
#pokud se cena posouva nasim smerem olespon o (0.05) nad (SL + 0.09val), posuneme SL o offset
|
||||||
#+ varianta - skoncit breakeven
|
#+ varianta - skoncit breakeven
|
||||||
@ -24,75 +25,68 @@ from v2realbot.strategyblocks.activetrade.helpers import get_signal_section_dire
|
|||||||
# SL_trailing_stop_at_breakeven_short = true
|
# SL_trailing_stop_at_breakeven_short = true
|
||||||
# SL_trailing_stop_at_breakeven_long = true
|
# SL_trailing_stop_at_breakeven_long = true
|
||||||
|
|
||||||
def trail_SL_management(state: StrategyState, accountsWithActiveTrade, data):
|
def trail_SL_management(state: StrategyState, data):
|
||||||
#iterate over accountsWithActiveTrade
|
if int(state.positions) != 0 and float(state.avgp)>0 and state.vars.pending is None:
|
||||||
for account_str, activeTrade in accountsWithActiveTrade.items():
|
|
||||||
positions = state.account_variables[account_str].positions
|
|
||||||
avgp = state.account_variables[account_str].avgp
|
|
||||||
pending = state.account_variables[account_str].pending
|
|
||||||
signal_name = activeTrade.generated_by
|
|
||||||
last_entry_index = state.account_variables[account_str].last_entry_index
|
|
||||||
if int(positions) != 0 and float(avgp)>0 and pending is None:
|
|
||||||
|
|
||||||
if int(positions) < 0:
|
if int(state.positions) < 0:
|
||||||
direction = TradeDirection.SHORT
|
direction = TradeDirection.SHORT
|
||||||
smer = "short"
|
smer = "short"
|
||||||
else:
|
else:
|
||||||
direction = TradeDirection.LONG
|
direction = TradeDirection.LONG
|
||||||
smer = "long"
|
smer = "long"
|
||||||
|
|
||||||
# zatim nastaveni SL plati pro vsechny - do budoucna per signal - pridat sekci
|
|
||||||
|
|
||||||
options = safe_get(state.vars, 'exit', None)
|
|
||||||
if options is None:
|
|
||||||
state.ilog(lvl=1,e="Trail SL. No options for exit conditions in stratvars.")
|
|
||||||
return
|
|
||||||
|
|
||||||
directive_name = 'SL_trailing_enabled_'+str(smer)
|
|
||||||
sl_trailing_enabled = get_signal_section_directive(state=state, signal_name=signal_name, directive_name=directive_name, default_value=safe_get(options, directive_name, False))
|
|
||||||
|
|
||||||
|
# zatim nastaveni SL plati pro vsechny - do budoucna per signal - pridat sekci
|
||||||
|
|
||||||
#SL_trailing_protection_window_short
|
options = safe_get(state.vars, 'exit', None)
|
||||||
directive_name = 'SL_trailing_protection_window_'+str(smer)
|
if options is None:
|
||||||
SL_trailing_protection_window = get_signal_section_directive(state=state, signal_name=signal_name, directive_name=directive_name, default_value=safe_get(options, directive_name, 0))
|
state.ilog(lvl=1,e="Trail SL. No options for exit conditions in stratvars.")
|
||||||
index_to_compare = int(last_entry_index)+int(SL_trailing_protection_window)
|
return
|
||||||
if index_to_compare > int(data["index"]):
|
|
||||||
state.ilog(lvl=1,e=f"SL trail PROTECTION WINDOW {SL_trailing_protection_window} - TOO SOON", currindex=data["index"], index_to_compare=index_to_compare, last_entry_index=last_entry_index)
|
directive_name = 'SL_trailing_enabled_'+str(smer)
|
||||||
|
sl_trailing_enabled = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(options, directive_name, False))
|
||||||
|
|
||||||
|
|
||||||
|
#SL_trailing_protection_window_short
|
||||||
|
directive_name = 'SL_trailing_protection_window_'+str(smer)
|
||||||
|
SL_trailing_protection_window = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(options, directive_name, 0))
|
||||||
|
index_to_compare = int(state.vars.last_in_index)+int(SL_trailing_protection_window)
|
||||||
|
if index_to_compare > int(data["index"]):
|
||||||
|
state.ilog(lvl=1,e=f"SL trail PROTECTION WINDOW {SL_trailing_protection_window} - TOO SOON", currindex=data["index"], index_to_compare=index_to_compare, last_in_index=state.vars.last_in_index)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if sl_trailing_enabled is True:
|
||||||
|
directive_name = 'SL_trailing_stop_at_breakeven_'+str(smer)
|
||||||
|
stop_breakeven = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(options, directive_name, False))
|
||||||
|
directive_name = 'SL_defval_'+str(smer)
|
||||||
|
def_SL = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(options, directive_name, 0.01))
|
||||||
|
directive_name = "SL_trailing_offset_"+str(smer)
|
||||||
|
offset = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(options, directive_name, 0.01))
|
||||||
|
directive_name = "SL_trailing_step_"+str(smer)
|
||||||
|
step = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(options, directive_name, offset))
|
||||||
|
|
||||||
|
#pokud je pozadovan trail jen do breakeven a uz prekroceno
|
||||||
|
if (direction == TradeDirection.LONG and stop_breakeven and state.vars.activeTrade.stoploss_value >= float(state.avgp)) or (direction == TradeDirection.SHORT and stop_breakeven and state.vars.activeTrade.stoploss_value <= float(state.avgp)):
|
||||||
|
state.ilog(lvl=1,e=f"SL trail STOP at breakeven {str(smer)} SL:{state.vars.activeTrade.stoploss_value} UNCHANGED", stop_breakeven=stop_breakeven)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if sl_trailing_enabled is True:
|
#Aktivace SL pokud vystoupa na "offset", a nasledne posunuti o "step"
|
||||||
directive_name = 'SL_trailing_stop_at_breakeven_'+str(smer)
|
|
||||||
stop_breakeven = get_signal_section_directive(state=state, signal_name=signal_name, directive_name=directive_name, default_value=safe_get(options, directive_name, False))
|
|
||||||
directive_name = 'SL_defval_'+str(smer)
|
|
||||||
def_SL = get_signal_section_directive(state=state, signal_name=signal_name, directive_name=directive_name, default_value=safe_get(options, directive_name, 0.01))
|
|
||||||
directive_name = "SL_trailing_offset_"+str(smer)
|
|
||||||
offset = get_signal_section_directive(state=state, signal_name=signal_name, directive_name=directive_name, default_value=safe_get(options, directive_name, 0.01))
|
|
||||||
directive_name = "SL_trailing_step_"+str(smer)
|
|
||||||
step = get_signal_section_directive(state=state, signal_name=signal_name, directive_name=directive_name, default_value=safe_get(options, directive_name, offset))
|
|
||||||
|
|
||||||
#pokud je pozadovan trail jen do breakeven a uz prekroceno
|
offset_normalized = normalize_tick(state, data, offset) #to ticks and from options
|
||||||
if (direction == TradeDirection.LONG and stop_breakeven and activeTrade.stoploss_value >= float(avgp)) or (direction == TradeDirection.SHORT and stop_breakeven and activeTrade.stoploss_value <= float(avgp)):
|
step_normalized = normalize_tick(state, data, step)
|
||||||
state.ilog(lvl=1,e=f"SL trail STOP at breakeven {str(smer)} SL:{activeTrade.stoploss_value} UNCHANGED", stop_breakeven=stop_breakeven)
|
def_SL_normalized = normalize_tick(state, data, def_SL)
|
||||||
return
|
if direction == TradeDirection.LONG:
|
||||||
|
move_SL_threshold = state.vars.activeTrade.stoploss_value + offset_normalized + def_SL_normalized
|
||||||
#Aktivace SL pokud vystoupa na "offset", a nasledne posunuti o "step"
|
state.ilog(lvl=1,e=f"SL TRAIL EVAL {smer} SL:{round(state.vars.activeTrade.stoploss_value,3)} TRAILGOAL:{move_SL_threshold}", def_SL=def_SL, offset=offset, offset_normalized=offset_normalized, step_normalized=step_normalized, def_SL_normalized=def_SL_normalized)
|
||||||
|
if (move_SL_threshold) < data['close']:
|
||||||
offset_normalized = normalize_tick(state, data, offset) #to ticks and from options
|
state.vars.activeTrade.stoploss_value += step_normalized
|
||||||
step_normalized = normalize_tick(state, data, step)
|
insert_SL_history(state)
|
||||||
def_SL_normalized = normalize_tick(state, data, def_SL)
|
state.ilog(lvl=1,e=f"SL TRAIL TH {smer} reached {move_SL_threshold} SL moved to {state.vars.activeTrade.stoploss_value}", offset_normalized=offset_normalized, step_normalized=step_normalized, def_SL_normalized=def_SL_normalized)
|
||||||
if direction == TradeDirection.LONG:
|
elif direction == TradeDirection.SHORT:
|
||||||
move_SL_threshold = activeTrade.stoploss_value + offset_normalized + def_SL_normalized
|
move_SL_threshold = state.vars.activeTrade.stoploss_value - offset_normalized - def_SL_normalized
|
||||||
state.ilog(lvl=1,e=f"SL TRAIL EVAL {smer} SL:{round(activeTrade.stoploss_value,3)} TRAILGOAL:{move_SL_threshold}", def_SL=def_SL, offset=offset, offset_normalized=offset_normalized, step_normalized=step_normalized, def_SL_normalized=def_SL_normalized)
|
state.ilog(lvl=0,e=f"SL TRAIL EVAL {smer} SL:{round(state.vars.activeTrade.stoploss_value,3)} TRAILGOAL:{move_SL_threshold}", def_SL=def_SL, offset=offset, offset_normalized=offset_normalized, step_normalized=step_normalized, def_SL_normalized=def_SL_normalized)
|
||||||
if (move_SL_threshold) < data['close']:
|
if (move_SL_threshold) > data['close']:
|
||||||
activeTrade.stoploss_value += step_normalized
|
state.vars.activeTrade.stoploss_value -= step_normalized
|
||||||
insert_SL_history(state, activeTrade)
|
insert_SL_history(state)
|
||||||
state.ilog(lvl=1,e=f"SL TRAIL TH {smer} reached {move_SL_threshold} SL moved to {activeTrade.stoploss_value}", offset_normalized=offset_normalized, step_normalized=step_normalized, def_SL_normalized=def_SL_normalized)
|
state.ilog(lvl=1,e=f"SL TRAIL GOAL {smer} reached {move_SL_threshold} SL moved to {state.vars.activeTrade.stoploss_value}", offset_normalized=offset_normalized, step_normalized=step_normalized, def_SL_normalized=def_SL_normalized)
|
||||||
elif direction == TradeDirection.SHORT:
|
|
||||||
move_SL_threshold = activeTrade.stoploss_value - offset_normalized - def_SL_normalized
|
|
||||||
state.ilog(lvl=0,e=f"SL TRAIL EVAL {smer} SL:{round(activeTrade.stoploss_value,3)} TRAILGOAL:{move_SL_threshold}", def_SL=def_SL, offset=offset, offset_normalized=offset_normalized, step_normalized=step_normalized, def_SL_normalized=def_SL_normalized)
|
|
||||||
if (move_SL_threshold) > data['close']:
|
|
||||||
activeTrade.stoploss_value -= step_normalized
|
|
||||||
insert_SL_history(state, activeTrade)
|
|
||||||
state.ilog(lvl=1,e=f"SL TRAIL GOAL {smer} reached {move_SL_threshold} SL moved to {activeTrade.stoploss_value}", offset_normalized=offset_normalized, step_normalized=step_normalized, def_SL_normalized=def_SL_normalized)
|
|
||||||
|
|||||||
@ -55,11 +55,7 @@ def populate_all_indicators(data, state: StrategyState):
|
|||||||
#TODO tento lof patri spis do nextu classic SL - je poplatny typu stratefie
|
#TODO tento lof patri spis do nextu classic SL - je poplatny typu stratefie
|
||||||
#TODO na toto se podivam, nejak moc zajasonovani a zpatky -
|
#TODO na toto se podivam, nejak moc zajasonovani a zpatky -
|
||||||
#PERF PROBLEM
|
#PERF PROBLEM
|
||||||
positions = state.account_variables[state.account].positions
|
state.ilog(lvl=1,e="ENTRY", msg=f"LP:{lp} P:{state.positions}/{round(float(state.avgp),3)} SL:{state.vars.activeTrade.stoploss_value if state.vars.activeTrade is not None else None} GP:{state.vars.activeTrade.goal_price if state.vars.activeTrade is not None else None} profit:{round(float(state.profit),2)} profit_rel:{round(np.sum(state.rel_profit_cum),6) if len(state.rel_profit_cum)>0 else 0} Trades:{len(state.tradeList)} pend:{state.vars.pending}", rel_profit_cum=str(state.rel_profit_cum), activeTrade=transform_data(state.vars.activeTrade, json_serial), prescribedTrades=transform_data(state.vars.prescribedTrades, json_serial), pending=str(state.vars.pending))
|
||||||
avgp = state.account_variables[state.account].avgp
|
|
||||||
#state.ilog(lvl=1,e="ENTRY", msg=f"LP:{lp} P:{positions}/{round(float(avgp),3)} SL:{state.vars.activeTrade.stoploss_value if state.vars.activeTrade is not None else None} GP:{state.vars.activeTrade.goal_price if state.vars.activeTrade is not None else None} profit:{round(float(state.profit),2)} profit_rel:{round(np.sum(state.rel_profit_cum),6) if len(state.rel_profit_cum)>0 else 0} Trades:{len(state.tradeList)} pend:{state.vars.pending}", rel_profit_cum=str(state.rel_profit_cum), activeTrade=transform_data(state.vars.activeTrade, json_serial), prescribedTrades=transform_data(state.vars.prescribedTrades, json_serial), pending=str(state.vars.pending))
|
|
||||||
|
|
||||||
state.ilog(lvl=1,e="ENTRY", msg=f"LP:{lp} ", accountVars=transform_data(state.account_variables, json_serial), prescribedTrades=transform_data(state.vars.prescribedTrades, json_serial))
|
|
||||||
|
|
||||||
#kroky pro CONFIRMED BAR only
|
#kroky pro CONFIRMED BAR only
|
||||||
if conf_bar == 1:
|
if conf_bar == 1:
|
||||||
|
|||||||
@ -46,10 +46,6 @@ def populate_dynamic_slopeLP_indicator(data, state: StrategyState, name):
|
|||||||
#typ leveho bodu [lastbuy - cena posledniho nakupu, baropen - cena otevreni baru]
|
#typ leveho bodu [lastbuy - cena posledniho nakupu, baropen - cena otevreni baru]
|
||||||
leftpoint = safe_get(options, 'leftpoint', "lastbuy")
|
leftpoint = safe_get(options, 'leftpoint', "lastbuy")
|
||||||
|
|
||||||
#REFACTOR multiaccount
|
|
||||||
#avgp bereme z primarni accountu (state.account)
|
|
||||||
avgp = state.account_variables[state.account].avgp
|
|
||||||
|
|
||||||
#lookback has to be even
|
#lookback has to be even
|
||||||
if lookback_offset % 2 != 0:
|
if lookback_offset % 2 != 0:
|
||||||
lookback_offset += 1
|
lookback_offset += 1
|
||||||
@ -69,8 +65,8 @@ def populate_dynamic_slopeLP_indicator(data, state: StrategyState, name):
|
|||||||
|
|
||||||
#pokud mame aktivni pozice, nastavime lookbackprice a time podle posledniho tradu
|
#pokud mame aktivni pozice, nastavime lookbackprice a time podle posledniho tradu
|
||||||
#pokud se ale dlouho nenakupuje (uplynulo od posledniho nakupu vic nez back_to_standard_after baru), tak se vracime k prumeru
|
#pokud se ale dlouho nenakupuje (uplynulo od posledniho nakupu vic nez back_to_standard_after baru), tak se vracime k prumeru
|
||||||
if avgp > 0 and state.bars.index[-1] < int(state.vars.last_entry_index)+back_to_standard_after:
|
if state.avgp > 0 and state.bars.index[-1] < int(state.vars.last_buy_index)+back_to_standard_after:
|
||||||
lb_index = -1 - (state.bars.index[-1] - int(state.vars.last_entry_index))
|
lb_index = -1 - (state.bars.index[-1] - int(state.vars.last_buy_index))
|
||||||
lookbackprice = state.bars.vwap[lb_index]
|
lookbackprice = state.bars.vwap[lb_index]
|
||||||
state.ilog(lvl=0,e=f"IND {name} slope {leftpoint}- LEFT POINT OVERRIDE bereme ajko cenu lastbuy {lookbackprice=} {lookbacktime=} {lb_index=}")
|
state.ilog(lvl=0,e=f"IND {name} slope {leftpoint}- LEFT POINT OVERRIDE bereme ajko cenu lastbuy {lookbackprice=} {lookbacktime=} {lb_index=}")
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
|
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
|
||||||
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
|
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
|
||||||
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
|
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff, create_new_bars, slice_dict_lists
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff, create_new_bars, slice_dict_lists
|
||||||
from v2realbot.utils.directive_utils import get_conditions_from_configuration
|
from v2realbot.utils.directive_utils import get_conditions_from_configuration
|
||||||
from v2realbot.common.model import SLHistory
|
from v2realbot.common.model import SLHistory
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
|
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
|
||||||
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
|
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
|
||||||
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
|
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff, create_new_bars, slice_dict_lists
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff, create_new_bars, slice_dict_lists
|
||||||
from v2realbot.utils.directive_utils import get_conditions_from_configuration
|
from v2realbot.utils.directive_utils import get_conditions_from_configuration
|
||||||
#import mlroom.utils.mlutils as ml
|
#import mlroom.utils.mlutils as ml
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
|
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
|
||||||
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
|
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
|
||||||
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
|
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff, create_new_bars, slice_dict_lists
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get, is_still, is_window_open, eval_cond_dict, crossed_down, crossed_up, crossed, is_pivot, json_serial, pct_diff, create_new_bars, slice_dict_lists
|
||||||
from v2realbot.utils.directive_utils import get_conditions_from_configuration
|
from v2realbot.utils.directive_utils import get_conditions_from_configuration
|
||||||
from v2realbot.common.model import SLHistory
|
from v2realbot.common.model import SLHistory
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
from v2realbot.common.model import TradeDirection, TradeStatus
|
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus
|
||||||
from v2realbot.utils.utils import zoneNY, json_serial,transform_data, gaka
|
from v2realbot.utils.utils import zoneNY, json_serial,transform_data
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
#import random
|
#import random
|
||||||
import orjson
|
import orjson
|
||||||
@ -10,109 +10,97 @@ from v2realbot.strategyblocks.indicators.helpers import value_or_indicator
|
|||||||
#TODO nad prescribed trades postavit vstupni funkce
|
#TODO nad prescribed trades postavit vstupni funkce
|
||||||
def execute_prescribed_trades(state: StrategyState, data):
|
def execute_prescribed_trades(state: StrategyState, data):
|
||||||
##evaluate prescribed trade, prvni eligible presuneme do activeTrade, zmenime stav and vytvorime objednavky
|
##evaluate prescribed trade, prvni eligible presuneme do activeTrade, zmenime stav and vytvorime objednavky
|
||||||
|
|
||||||
#for multiaccount setup we check if there is active trade for each account
|
|
||||||
|
|
||||||
if len(state.vars.prescribedTrades) == 0 :
|
if state.vars.activeTrade is not None or len(state.vars.prescribedTrades) == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
accountsWithNoActiveTrade = gaka(state.account_variables, "activeTrade", None, lambda x: x is None)
|
|
||||||
|
|
||||||
if len(accountsWithNoActiveTrade.values()) == 0:
|
|
||||||
#print("active trades on all accounts")
|
|
||||||
return
|
|
||||||
|
|
||||||
#returns true if all values are not None
|
|
||||||
#all(v is not None for v in d.keys())
|
|
||||||
|
|
||||||
#evaluate long (price/market)
|
#evaluate long (price/market)
|
||||||
#support multiaccount trades
|
|
||||||
state.ilog(lvl=1,e="evaluating prescr trades", trades=transform_data(state.vars.prescribedTrades, json_serial))
|
state.ilog(lvl=1,e="evaluating prescr trades", trades=transform_data(state.vars.prescribedTrades, json_serial))
|
||||||
for trade in state.vars.prescribedTrades:
|
for trade in state.vars.prescribedTrades:
|
||||||
if trade.account.name not in accountsWithNoActiveTrade.keys() or state.account_variables[trade.account.name].pending is not None: #availability or pending
|
|
||||||
continue
|
|
||||||
if trade.status == TradeStatus.READY and trade.direction == TradeDirection.LONG and (trade.entry_price is None or trade.entry_price >= data['close']):
|
if trade.status == TradeStatus.READY and trade.direction == TradeDirection.LONG and (trade.entry_price is None or trade.entry_price >= data['close']):
|
||||||
trade.status = TradeStatus.ACTIVATED
|
trade.status = TradeStatus.ACTIVATED
|
||||||
trade.last_update = datetime.fromtimestamp(state.time).astimezone(zoneNY)
|
trade.last_update = datetime.fromtimestamp(state.time).astimezone(zoneNY)
|
||||||
state.ilog(lvl=1,e=f"evaluated LONG", trade=transform_data(trade, json_serial), prescrTrades=transform_data(state.vars.prescribedTrades, json_serial))
|
state.ilog(lvl=1,e=f"evaluated LONG", trade=transform_data(trade, json_serial), prescrTrades=transform_data(state.vars.prescribedTrades, json_serial))
|
||||||
execute_trade(state, data, trade) #TBD ERROR HANDLING
|
state.vars.activeTrade = trade
|
||||||
del accountsWithNoActiveTrade[trade.account.name] #to avoid other entries on the same account
|
state.vars.last_buy_index = data["index"]
|
||||||
continue
|
state.vars.last_in_index = data["index"]
|
||||||
|
break
|
||||||
#evaluate shorts
|
#evaluate shorts
|
||||||
if trade.status == TradeStatus.READY and trade.direction == TradeDirection.SHORT and (trade.entry_price is None or trade.entry_price <= data['close']):
|
if not state.vars.activeTrade:
|
||||||
state.ilog(lvl=1,e=f"evaluaed SHORT", trade=transform_data(trade, json_serial), prescrTrades=transform_data(state.vars.prescribedTrades, json_serial))
|
for trade in state.vars.prescribedTrades:
|
||||||
trade.status = TradeStatus.ACTIVATED
|
if trade.status == TradeStatus.READY and trade.direction == TradeDirection.SHORT and (trade.entry_price is None or trade.entry_price <= data['close']):
|
||||||
trade.last_update = datetime.fromtimestamp(state.time).astimezone(zoneNY)
|
state.ilog(lvl=1,e=f"evaluaed SHORT", trade=transform_data(trade, json_serial), prescrTrades=transform_data(state.vars.prescribedTrades, json_serial))
|
||||||
execute_trade(state, data, trade) #TBD ERROR HANDLING
|
trade.status = TradeStatus.ACTIVATED
|
||||||
del accountsWithNoActiveTrade[trade.account.name] #to avoid other entries on the same account
|
trade.last_update = datetime.fromtimestamp(state.time).astimezone(zoneNY)
|
||||||
continue
|
state.vars.activeTrade = trade
|
||||||
|
state.vars.last_buy_index = data["index"]
|
||||||
|
state.vars.last_in_index = data["index"]
|
||||||
|
break
|
||||||
|
|
||||||
|
#odeslani ORDER + NASTAVENI STOPLOSS (zatim hardcoded)
|
||||||
|
if state.vars.activeTrade:
|
||||||
|
if state.vars.activeTrade.direction == TradeDirection.LONG:
|
||||||
|
state.ilog(lvl=1,e="odesilame LONG ORDER", trade=transform_data(state.vars.activeTrade, json_serial))
|
||||||
|
if state.vars.activeTrade.size is not None:
|
||||||
|
size = state.vars.activeTrade.size
|
||||||
|
else:
|
||||||
|
size = state.vars.chunk
|
||||||
|
res = state.buy(size=size)
|
||||||
|
if isinstance(res, int) and res < 0:
|
||||||
|
raise Exception(f"error in required operation LONG {res}")
|
||||||
|
|
||||||
#TODO konzolidovat nize na spolecny kod pro short a long
|
#defaultni goal price pripadne nastavujeme az v notifikaci
|
||||||
#odeslani ORDER + NASTAVENI STOPLOSS (zatim hardcoded)
|
|
||||||
#TODO doplnit error management
|
|
||||||
def execute_trade(state, data, trade):
|
|
||||||
if trade.direction == TradeDirection.LONG:
|
|
||||||
state.ilog(lvl=1,e="odesilame LONG ORDER", trade=transform_data(trade, json_serial))
|
|
||||||
size = trade.size if trade.size is not None else state.vars.chunk
|
|
||||||
res = state.buy(size=size, account=trade.account)
|
|
||||||
#TODO ukládáme někam ID objednávky? už zde je vráceno v res
|
|
||||||
#TODO error handling
|
|
||||||
if isinstance(res, int) and res < 0:
|
|
||||||
raise Exception(f"error in required operation LONG {res}")
|
|
||||||
#TODO error handling
|
|
||||||
#defaultni goal price pripadne nastavujeme az v notifikaci
|
|
||||||
state.account_variables[trade.account.name].activeTrade = trade
|
|
||||||
|
|
||||||
#TODO nastaveni SL az do notifikace, kdy je známá
|
#TODO nastaveni SL az do notifikace, kdy je známá
|
||||||
#pokud neni nastaveno SL v prescribe, tak nastavuji default dle stratvars
|
#pokud neni nastaveno SL v prescribe, tak nastavuji default dle stratvars
|
||||||
if trade.stoploss_value is None:
|
if state.vars.activeTrade.stoploss_value is None:
|
||||||
sl_defvalue = get_default_sl_value(state=state, signal_name=trade.generated_by, direction=trade.direction)
|
sl_defvalue = get_default_sl_value(state, direction=state.vars.activeTrade.direction)
|
||||||
|
|
||||||
if isinstance(sl_defvalue, (float, int)):
|
if isinstance(sl_defvalue, (float, int)):
|
||||||
#normalizuji dle aktualni ceny
|
#normalizuji dle aktualni ceny
|
||||||
sl_defvalue_normalized = normalize_tick(state, data,sl_defvalue)
|
sl_defvalue_normalized = normalize_tick(state, data,sl_defvalue)
|
||||||
state.account_variables[trade.account.name].activeTrade.stoploss_value = float(data['close']) - sl_defvalue_normalized
|
state.vars.activeTrade.stoploss_value = float(data['close']) - sl_defvalue_normalized
|
||||||
state.ilog(lvl=1,e=f"Nastaveno SL na {sl_defvalue}, priced normalized: {sl_defvalue_normalized} price: {state.account_variables[trade.account.name].activeTrade.stoploss_value }")
|
state.ilog(lvl=1,e=f"Nastaveno SL na {sl_defvalue}, priced normalized: {sl_defvalue_normalized} price: {state.vars.activeTrade.stoploss_value }")
|
||||||
elif isinstance(sl_defvalue, str):
|
elif isinstance(sl_defvalue, str):
|
||||||
#from indicator
|
#from indicator
|
||||||
ind = sl_defvalue
|
ind = sl_defvalue
|
||||||
sl_defvalue_abs = float(value_or_indicator(state, sl_defvalue))
|
sl_defvalue_abs = float(value_or_indicator(state, sl_defvalue))
|
||||||
if sl_defvalue_abs >= float(data['close']):
|
if sl_defvalue_abs >= float(data['close']):
|
||||||
raise Exception(f"error in stoploss {ind} {sl_defvalue_abs} >= curr price")
|
raise Exception(f"error in stoploss {ind} {sl_defvalue_abs} >= curr price")
|
||||||
state.account_variables[trade.account.name].activeTrade.stoploss_value = sl_defvalue_abs
|
state.vars.activeTrade.stoploss_value = sl_defvalue_abs
|
||||||
state.ilog(lvl=1,e=f"Nastaveno SL na {sl_defvalue_abs} dle indikatoru {ind}")
|
state.ilog(lvl=1,e=f"Nastaveno SL na {sl_defvalue_abs} dle indikatoru {ind}")
|
||||||
insert_SL_history(state, state.account_variables[trade.account.name].activeTrade)
|
insert_SL_history(state)
|
||||||
elif trade.direction == TradeDirection.SHORT:
|
state.vars.pending = state.vars.activeTrade.id
|
||||||
state.ilog(lvl=1,e="odesilame SHORT ORDER", trade=transform_data(trade, json_serial))
|
elif state.vars.activeTrade.direction == TradeDirection.SHORT:
|
||||||
size = trade.size if trade.size is not None else state.vars.chunk
|
state.ilog(lvl=1,e="odesilame SHORT ORDER", trade=transform_data(state.vars.activeTrade, json_serial))
|
||||||
res = state.sell(size=size, account=trade.account)
|
if state.vars.activeTrade.size is not None:
|
||||||
if isinstance(res, int) and res < 0:
|
size = state.vars.activeTrade.size
|
||||||
print(f"error in required operation SHORT {res}")
|
else:
|
||||||
raise Exception(f"error in required operation SHORT {res}")
|
size = state.vars.chunk
|
||||||
#defaultní goalprice nastavujeme az v notifikaci
|
res = state.sell(size=size)
|
||||||
|
if isinstance(res, int) and res < 0:
|
||||||
|
print(f"error in required operation SHORT {res}")
|
||||||
|
raise Exception(f"error in required operation SHORT {res}")
|
||||||
|
#defaultní goalprice nastavujeme az v notifikaci
|
||||||
|
|
||||||
state.account_variables[trade.account.name].activeTrade = trade
|
#pokud neni nastaveno SL v prescribe, tak nastavuji default dle stratvars
|
||||||
#pokud neni nastaveno SL v prescribe, tak nastavuji default dle stratvars
|
if state.vars.activeTrade.stoploss_value is None:
|
||||||
if trade.stoploss_value is None:
|
sl_defvalue = get_default_sl_value(state, direction=state.vars.activeTrade.direction)
|
||||||
sl_defvalue = get_default_sl_value(state, signal_name=trade.generated_by,direction=trade.direction)
|
|
||||||
|
|
||||||
if isinstance(sl_defvalue, (float, int)):
|
if isinstance(sl_defvalue, (float, int)):
|
||||||
#normalizuji dle aktualni ceny
|
#normalizuji dle aktualni ceny
|
||||||
sl_defvalue_normalized = normalize_tick(state, data,sl_defvalue)
|
sl_defvalue_normalized = normalize_tick(state, data,sl_defvalue)
|
||||||
state.account_variables[trade.account.name].activeTrade.stoploss_value = float(data['close']) + sl_defvalue_normalized
|
state.vars.activeTrade.stoploss_value = float(data['close']) + sl_defvalue_normalized
|
||||||
state.ilog(lvl=1,e=f"Nastaveno SL na {sl_defvalue}, priced normalized: {sl_defvalue_normalized} price: {state.account_variables[trade.account.name].activeTrade.stoploss_value }")
|
state.ilog(lvl=1,e=f"Nastaveno SL na {sl_defvalue}, priced normalized: {sl_defvalue_normalized} price: {state.vars.activeTrade.stoploss_value }")
|
||||||
elif isinstance(sl_defvalue, str):
|
elif isinstance(sl_defvalue, str):
|
||||||
#from indicator
|
#from indicator
|
||||||
ind = sl_defvalue
|
ind = sl_defvalue
|
||||||
sl_defvalue_abs = float(value_or_indicator(state, sl_defvalue))
|
sl_defvalue_abs = float(value_or_indicator(state, sl_defvalue))
|
||||||
if sl_defvalue_abs <= float(data['close']):
|
if sl_defvalue_abs <= float(data['close']):
|
||||||
raise Exception(f"error in stoploss {ind} {sl_defvalue_abs} <= curr price")
|
raise Exception(f"error in stoploss {ind} {sl_defvalue_abs} <= curr price")
|
||||||
state.account_variables[trade.account.name].activeTrade.stoploss_value = sl_defvalue_abs
|
state.vars.activeTrade.stoploss_value = sl_defvalue_abs
|
||||||
state.ilog(lvl=1,e=f"Nastaveno SL na {sl_defvalue_abs} dle indikatoru {ind}")
|
state.ilog(lvl=1,e=f"Nastaveno SL na {sl_defvalue_abs} dle indikatoru {ind}")
|
||||||
insert_SL_history(state, state.account_variables[trade.account.name].activeTrade)
|
insert_SL_history(state)
|
||||||
|
state.vars.pending = state.vars.activeTrade.id
|
||||||
state.account_variables[trade.account.name].pending = trade.id
|
else:
|
||||||
state.account_variables[trade.account.name].activeTrade = trade
|
state.ilog(lvl=1,e="unknow direction")
|
||||||
state.account_variables[trade.account.name].last_entry_index =data["index"] #last_entry_index per account
|
state.vars.activeTrade = None
|
||||||
state.vars.last_entry_index = data["index"] #spolecne pro vsechny accounty
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
|
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get, gaka
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get
|
||||||
from v2realbot.config import KW
|
from v2realbot.config import KW
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@ -31,12 +31,6 @@ def signal_search(state: StrategyState, data):
|
|||||||
# slope10.out_short_if_above = 0
|
# slope10.out_short_if_above = 0
|
||||||
# ema.AND.short_if_below = 28
|
# ema.AND.short_if_below = 28
|
||||||
|
|
||||||
accountsWithNoActiveTrade = gaka(state.account_variables, "activeTrade", None, lambda x: x is None)
|
|
||||||
|
|
||||||
if len(accountsWithNoActiveTrade.values()) == 0:
|
|
||||||
#print("active trades on all accounts")
|
|
||||||
return
|
|
||||||
|
|
||||||
for signalname, signalsettings in state.vars.signals.items():
|
for signalname, signalsettings in state.vars.signals.items():
|
||||||
execute_signal_generator(state, data, signalname)
|
execute_signal_generator(state, data, signalname)
|
||||||
|
|
||||||
@ -44,21 +38,14 @@ def signal_search(state: StrategyState, data):
|
|||||||
# pokud je s cenou ceka se na cenu, pokud immmediate tak se hned provede
|
# pokud je s cenou ceka se na cenu, pokud immmediate tak se hned provede
|
||||||
# to vse za predpokladu, ze neni aktivni trade
|
# to vse za predpokladu, ze neni aktivni trade
|
||||||
|
|
||||||
def execute_signal_generator(state: StrategyState, data, name):
|
def execute_signal_generator(state, data, name):
|
||||||
state.ilog(lvl=1,e=f"SIGNAL SEARCH for {name}", cond_go=state.vars.conditions[KW.go][name], cond_dontgo=state.vars.conditions[KW.dont_go][name], cond_activate=state.vars.conditions[KW.activate][name] )
|
state.ilog(lvl=1,e=f"SIGNAL SEARCH for {name}", cond_go=state.vars.conditions[KW.go][name], cond_dontgo=state.vars.conditions[KW.dont_go][name], cond_activate=state.vars.conditions[KW.activate][name] )
|
||||||
options = safe_get(state.vars.signals, name, None)
|
options = safe_get(state.vars.signals, name, None)
|
||||||
|
|
||||||
#add account from stratvars (if there) or default to self.state.account
|
|
||||||
|
|
||||||
if options is None:
|
if options is None:
|
||||||
state.ilog(lvl=1,e=f"No options for {name} in stratvars")
|
state.ilog(lvl=1,e=f"No options for {name} in stratvars")
|
||||||
return
|
return
|
||||||
|
|
||||||
#get account of the signal, fallback to default
|
|
||||||
account = safe_get(options, "account", state.account)
|
|
||||||
account_long = safe_get(options, "account_long", account)
|
|
||||||
account_short = safe_get(options, "account_short", account)
|
|
||||||
|
|
||||||
if common_go_preconditions_check(state, data, signalname=name, options=options) is False:
|
if common_go_preconditions_check(state, data, signalname=name, options=options) is False:
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -84,12 +71,9 @@ def execute_signal_generator(state: StrategyState, data, name):
|
|||||||
state.ilog(lvl=1,e=f"{name} SHORT DISABLED")
|
state.ilog(lvl=1,e=f"{name} SHORT DISABLED")
|
||||||
if long_enabled is False:
|
if long_enabled is False:
|
||||||
state.ilog(lvl=1,e=f"{name} LONG DISABLED")
|
state.ilog(lvl=1,e=f"{name} LONG DISABLED")
|
||||||
trade_made = None
|
if long_enabled and go_conditions_met(state, data,signalname=name, direction=TradeDirection.LONG):
|
||||||
#predkontroloa zda neni pending na accountu nebo aktivni trade
|
|
||||||
if state.account_variables[account_long].pending is None and state.account_variables[account_long].activeTrade is None and long_enabled and go_conditions_met(state, data,signalname=name, direction=TradeDirection.LONG):
|
|
||||||
multiplier = get_multiplier(state, data, options, TradeDirection.LONG)
|
multiplier = get_multiplier(state, data, options, TradeDirection.LONG)
|
||||||
state.vars.prescribedTrades.append(Trade(
|
state.vars.prescribedTrades.append(Trade(
|
||||||
account = account_long,
|
|
||||||
id=uuid4(),
|
id=uuid4(),
|
||||||
last_update=datetime.fromtimestamp(state.time).astimezone(zoneNY),
|
last_update=datetime.fromtimestamp(state.time).astimezone(zoneNY),
|
||||||
status=TradeStatus.READY,
|
status=TradeStatus.READY,
|
||||||
@ -99,12 +83,9 @@ def execute_signal_generator(state: StrategyState, data, name):
|
|||||||
direction=TradeDirection.LONG,
|
direction=TradeDirection.LONG,
|
||||||
entry_price=None,
|
entry_price=None,
|
||||||
stoploss_value = None))
|
stoploss_value = None))
|
||||||
trade_made = account_long
|
elif short_enabled and go_conditions_met(state, data, signalname=name, direction=TradeDirection.SHORT):
|
||||||
#pri multiaccountu muzeme udelat v jedne iteraci vice tradu avsak vzdy na ruznych accountech
|
|
||||||
if (trade_made is None or trade_made != account_short) and state.account_variables[account_short].pending is None and state.account_variables[account_short].activeTrade is None and short_enabled and go_conditions_met(state, data, signalname=name, direction=TradeDirection.SHORT):
|
|
||||||
multiplier = get_multiplier(state, data, options, TradeDirection.SHORT)
|
multiplier = get_multiplier(state, data, options, TradeDirection.SHORT)
|
||||||
state.vars.prescribedTrades.append(Trade(
|
state.vars.prescribedTrades.append(Trade(
|
||||||
account=account_short,
|
|
||||||
id=uuid4(),
|
id=uuid4(),
|
||||||
last_update=datetime.fromtimestamp(state.time).astimezone(zoneNY),
|
last_update=datetime.fromtimestamp(state.time).astimezone(zoneNY),
|
||||||
status=TradeStatus.READY,
|
status=TradeStatus.READY,
|
||||||
@ -114,5 +95,5 @@ def execute_signal_generator(state: StrategyState, data, name):
|
|||||||
direction=TradeDirection.SHORT,
|
direction=TradeDirection.SHORT,
|
||||||
entry_price=None,
|
entry_price=None,
|
||||||
stoploss_value = None))
|
stoploss_value = None))
|
||||||
return
|
else:
|
||||||
state.ilog(lvl=0,e=f"{name} NO SIGNAL")
|
state.ilog(lvl=0,e=f"{name} NO SIGNAL")
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
from v2realbot.strategy.base import StrategyState
|
from v2realbot.strategy.base import StrategyState
|
||||||
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
|
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
|
||||||
import v2realbot.utils.utils as utls
|
import v2realbot.utils.utils as utls
|
||||||
from v2realbot.config import KW
|
from v2realbot.config import KW
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
@ -99,8 +99,7 @@ def get_multiplier(state: StrategyState, data, signaloptions: dict, direction: T
|
|||||||
|
|
||||||
if probe_enabled:
|
if probe_enabled:
|
||||||
#zatim pouze probe number 1 natvrdo, tzn. nesmi byt trade pro aktivace
|
#zatim pouze probe number 1 natvrdo, tzn. nesmi byt trade pro aktivace
|
||||||
#zatim funguje pouze pro primarni
|
if state.vars.last_in_index is None:
|
||||||
if state.account_variables[state.account].last_entry_index is None:
|
|
||||||
#probe_number = utls.safe_get(options, "probe_number",1)
|
#probe_number = utls.safe_get(options, "probe_number",1)
|
||||||
probe_size = float(utls.safe_get(options, "probe_size", 0.1))
|
probe_size = float(utls.safe_get(options, "probe_size", 0.1))
|
||||||
state.ilog(lvl=1,e=f"SIZER - PROBE - setting multiplier to {probe_size}", options=options)
|
state.ilog(lvl=1,e=f"SIZER - PROBE - setting multiplier to {probe_size}", options=options)
|
||||||
|
|||||||
@ -12,7 +12,7 @@ from enum import Enum
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import v2realbot.controller.services as cs
|
import v2realbot.controller.services as cs
|
||||||
#from rich import print
|
#from rich import print
|
||||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
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
|
import v2realbot.controller.services as cs
|
||||||
from rich import print as richprint
|
from rich import print as richprint
|
||||||
from v2realbot.common.model import AnalyzerInputs
|
from v2realbot.common.model import AnalyzerInputs
|
||||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
|
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 scipy.stats import zscore
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from typing import Tuple, Optional, List
|
from typing import Tuple, Optional, List
|
||||||
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
|
||||||
from collections import Counter
|
from collections import Counter
|
||||||
import vectorbtpro as vbt
|
import vectorbtpro as vbt
|
||||||
|
|
||||||
|
|||||||
@ -9,8 +9,8 @@ import decimal
|
|||||||
from v2realbot.enums.enums import RecordType, Mode, StartBarAlign
|
from v2realbot.enums.enums import RecordType, Mode, StartBarAlign
|
||||||
import pickle
|
import pickle
|
||||||
import os
|
import os
|
||||||
from v2realbot.common.model import StrategyInstance, Runner, RunArchive, RunArchiveDetail, Intervals, SLHistory, InstantIndicator, AccountVariables
|
from v2realbot.common.model import StrategyInstance, Runner, RunArchive, RunArchiveDetail, Intervals, SLHistory, InstantIndicator
|
||||||
from v2realbot.common.model import Trade, TradeDirection, TradeStatus, TradeStoplossType
|
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
|
||||||
from typing import List
|
from typing import List
|
||||||
import tomli
|
import tomli
|
||||||
from v2realbot.config import DATA_DIR, ACCOUNT1_PAPER_API_KEY, ACCOUNT1_PAPER_SECRET_KEY
|
from v2realbot.config import DATA_DIR, ACCOUNT1_PAPER_API_KEY, ACCOUNT1_PAPER_SECRET_KEY
|
||||||
@ -36,220 +36,6 @@ import shutil
|
|||||||
from filelock import FileLock
|
from filelock import FileLock
|
||||||
import v2realbot.utils.config_handler as cfh
|
import v2realbot.utils.config_handler as cfh
|
||||||
import pandas_market_calendars as mcal
|
import pandas_market_calendars as mcal
|
||||||
from typing import Dict, Any, Callable, Optional
|
|
||||||
from pydantic import BaseModel
|
|
||||||
|
|
||||||
def get_attribute(obj: Any, attr: str) -> Any:
|
|
||||||
"""
|
|
||||||
Returns the value of given attribute from the object being it dict or BaseModel
|
|
||||||
"""
|
|
||||||
if isinstance(obj, dict):
|
|
||||||
return obj.get(attr)
|
|
||||||
if isinstance(obj, BaseModel):
|
|
||||||
return getattr(obj, attr, None)
|
|
||||||
return None
|
|
||||||
|
|
||||||
def gaka(
|
|
||||||
account_variables: Dict[str, Any],
|
|
||||||
name_of_attribute: str,
|
|
||||||
transform_function: Optional[Callable[[Any], Any]] = None,
|
|
||||||
condition_function: Optional[Callable[[Any], bool]] = None
|
|
||||||
) -> Dict[str, Any]:
|
|
||||||
"""
|
|
||||||
Gets Account Keyed Attribute
|
|
||||||
Extracts the specified attribute from each account variable in the given dictionary.
|
|
||||||
It also contains transformation function and condition function.
|
|
||||||
|
|
||||||
```
|
|
||||||
avgps = gaka(account_variables, "avgp",
|
|
||||||
transform_function=lambda x: round(x, 3),
|
|
||||||
condition_function=lambda x: x > 3)
|
|
||||||
|
|
||||||
returns:
|
|
||||||
{
|
|
||||||
'account2': 5000.654,
|
|
||||||
'account3': 3000.789,
|
|
||||||
'account4': 8000.235
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Args:
|
|
||||||
account_variables (Dict[str, BaseModel]): A dictionary of account variables.
|
|
||||||
name_of_attribute (str): The name of the attribute to extract.
|
|
||||||
transform_function (Optional[Callable[[Any], Any]]): Optional function to transform the attribute value.
|
|
||||||
condition_function (Optional[Callable[[Any], bool]]): Optional function to filter the results.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dict[str, Any]: A dictionary containing the extracted attribute for each account that meets the condition.
|
|
||||||
"""
|
|
||||||
result = {}
|
|
||||||
for account_str, acc_vars in account_variables.items():
|
|
||||||
value = get_attribute(acc_vars, name_of_attribute)
|
|
||||||
|
|
||||||
if value is None and not hasattr(acc_vars, name_of_attribute):
|
|
||||||
continue
|
|
||||||
|
|
||||||
transformed_value = transform_function(value) if transform_function else value
|
|
||||||
|
|
||||||
if condition_function is None or condition_function(transformed_value):
|
|
||||||
result[account_str] = transformed_value
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def gaka_unoptimized(
|
|
||||||
account_variables: Dict[str, Any],
|
|
||||||
name_of_attribute: str,
|
|
||||||
transform_function: Optional[Callable[[Any], Any]] = None,
|
|
||||||
condition_function: Optional[Callable[[Any], bool]] = None
|
|
||||||
) -> Dict[str, Any]:
|
|
||||||
"""
|
|
||||||
Gets Account Keyed Attribute
|
|
||||||
Extracts the specified attribute from each account variable in the given dictionary.
|
|
||||||
It also contains transformation function and condition function.
|
|
||||||
|
|
||||||
```
|
|
||||||
avgps = gaka(account_variables, "avgp",
|
|
||||||
transform_function=lambda x: round(x, 3),
|
|
||||||
condition_function=lambda x: x > 3)
|
|
||||||
|
|
||||||
returns:
|
|
||||||
{
|
|
||||||
'account2': 5000.654,
|
|
||||||
'account3': 3000.789,
|
|
||||||
'account4': 8000.235
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Args:
|
|
||||||
account_variables (Dict[str, BaseModel]): A dictionary of account variables.
|
|
||||||
name_of_attribute (str): The name of the attribute to extract.
|
|
||||||
transform_function (Optional[Callable[[Any], Any]]): Optional function to transform the attribute value.
|
|
||||||
condition_function (Optional[Callable[[Any], bool]]): Optional function to filter the results.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dict[str, Any]: A dictionary containing the extracted attribute for each account that meets the condition.
|
|
||||||
"""
|
|
||||||
result = {}
|
|
||||||
for account, acc_vars in account_variables.items():
|
|
||||||
if isinstance(acc_vars, BaseModel):
|
|
||||||
value = getattr(acc_vars, name_of_attribute, None)
|
|
||||||
elif isinstance(acc_vars, dict):
|
|
||||||
value = acc_vars.get(name_of_attribute, None)
|
|
||||||
else:
|
|
||||||
continue # Skip if acc_vars is neither BaseModel nor dict
|
|
||||||
|
|
||||||
if value is None and not hasattr(acc_vars, name_of_attribute):
|
|
||||||
continue # Skip if attribute doesn't exist
|
|
||||||
|
|
||||||
transformed_value = transform_function(value) if transform_function else value
|
|
||||||
|
|
||||||
if condition_function is None or condition_function(transformed_value):
|
|
||||||
result[account] = transformed_value
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def gaka_old_with_comprehesion(
|
|
||||||
account_variables: Dict[str, Any],
|
|
||||||
name_of_attribute: str,
|
|
||||||
transform_function: Optional[Callable[[Any], Any]] = None,
|
|
||||||
condition_function: Optional[Callable[[Any], bool]] = None
|
|
||||||
) -> Dict[str, Any]:
|
|
||||||
"""
|
|
||||||
Gets Account Keyed Attribute
|
|
||||||
Extracts the specified attribute from each account variable in the given dictionary.
|
|
||||||
|
|
||||||
It also contains transformation function and condition function.
|
|
||||||
|
|
||||||
```
|
|
||||||
avgps = gaka(account_variables, "avgp",
|
|
||||||
transform_function=lambda x: round(x, 3),
|
|
||||||
condition_function=lambda x: x > 3)
|
|
||||||
|
|
||||||
returns:
|
|
||||||
{
|
|
||||||
'account2': 5000.654,
|
|
||||||
'account3': 3000.789,
|
|
||||||
'account4': 8000.235
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Args:
|
|
||||||
account_variables (Dict[str, BaseModel]): A dictionary of account variables.
|
|
||||||
name_of_attribute (str): The name of the attribute to extract.
|
|
||||||
transform_function (Optional[Callable[[Any], Any]]): Optional function to transform the attribute value.
|
|
||||||
condition_function (Optional[Callable[[Any], bool]]): Optional function to filter the results.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dict[str, Any]: A dictionary containing the extracted attribute for each account that meets the condition.
|
|
||||||
"""
|
|
||||||
return {
|
|
||||||
account: transformed_value
|
|
||||||
for account, acc_vars in account_variables.items()
|
|
||||||
if (value := (
|
|
||||||
getattr(acc_vars, name_of_attribute, None)
|
|
||||||
if isinstance(acc_vars, BaseModel)
|
|
||||||
else acc_vars.get(name_of_attribute, None)
|
|
||||||
if isinstance(acc_vars, dict)
|
|
||||||
else None
|
|
||||||
)) is not None or name_of_attribute in acc_vars.__dict__
|
|
||||||
and (transformed_value := (
|
|
||||||
transform_function(value) if transform_function else value
|
|
||||||
)) is not None
|
|
||||||
and (not condition_function or condition_function(transformed_value))
|
|
||||||
}
|
|
||||||
|
|
||||||
def gaka_old(account_variables: Dict[str, Any], name_of_attribute: str, transform_function: Optional[Callable[[Any], Any]] = None) -> Dict[str, Any]:
|
|
||||||
"""
|
|
||||||
Gets Account Keyed Attribute
|
|
||||||
Extracts the specified attribute from each account variable in the given dictionary.
|
|
||||||
|
|
||||||
It also contain transformation function.
|
|
||||||
|
|
||||||
```
|
|
||||||
avgps = extract_attribute(account_variables, "avgp", lambda x: round(x, 3))
|
|
||||||
|
|
||||||
returns:
|
|
||||||
{
|
|
||||||
'account1': 1000.123,
|
|
||||||
'account2': 5000.654,
|
|
||||||
'account3': 3000.789,
|
|
||||||
'account4': 8000.235
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Args:
|
|
||||||
account_variables (Dict[str, BaseModel]): A dictionary of account variables.
|
|
||||||
name_of_attribute (str): The name of the attribute to extract.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dict[str, Any]: A dictionary containing the extracted attribute for each account.
|
|
||||||
"""
|
|
||||||
#return {account: getattr(acc_vars, name_of_attribute) for account, acc_vars in account_variables.items()}
|
|
||||||
return {
|
|
||||||
account: (
|
|
||||||
transform_function(value) if transform_function and value is not None else value
|
|
||||||
)
|
|
||||||
for account, acc_vars in account_variables.items()
|
|
||||||
if (value := (
|
|
||||||
getattr(acc_vars, name_of_attribute, None)
|
|
||||||
if isinstance(acc_vars, BaseModel)
|
|
||||||
else acc_vars.get(name_of_attribute, None)
|
|
||||||
if isinstance(acc_vars, dict)
|
|
||||||
else None
|
|
||||||
)) is not None
|
|
||||||
}
|
|
||||||
|
|
||||||
def empty_lists_in_dict(d: dict):
|
|
||||||
"""
|
|
||||||
Assumes all values of dict are list. Returns true if all lists are empty.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
d (dict): The dictionary to check.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: True if all lists in the dictionary are empty, False otherwise.
|
|
||||||
"""
|
|
||||||
return all(len(v) == 0 for v in d.values())
|
|
||||||
|
|
||||||
def validate_and_format_time(time_string):
|
def validate_and_format_time(time_string):
|
||||||
"""
|
"""
|
||||||
@ -889,7 +675,6 @@ def json_serial(obj):
|
|||||||
SLHistory: lambda obj: obj.__dict__,
|
SLHistory: lambda obj: obj.__dict__,
|
||||||
InstantIndicator: lambda obj: obj.__dict__,
|
InstantIndicator: lambda obj: obj.__dict__,
|
||||||
StrategyInstance: lambda obj: obj.__dict__,
|
StrategyInstance: lambda obj: obj.__dict__,
|
||||||
AccountVariables: lambda obj: obj.__dict__
|
|
||||||
}
|
}
|
||||||
|
|
||||||
serializer = type_map.get(type(obj))
|
serializer = type_map.get(type(obj))
|
||||||
|
|||||||
@ -1,255 +0,0 @@
|
|||||||
absl-py==2.0.0
|
|
||||||
alpaca==1.0.0
|
|
||||||
alpaca-py==0.18.1
|
|
||||||
altair==4.2.2
|
|
||||||
annotated-types==0.6.0
|
|
||||||
anyio==3.6.2
|
|
||||||
appdirs==1.4.4
|
|
||||||
appnope==0.1.3
|
|
||||||
APScheduler==3.10.4
|
|
||||||
argon2-cffi==23.1.0
|
|
||||||
argon2-cffi-bindings==21.2.0
|
|
||||||
arrow==1.3.0
|
|
||||||
asttokens==2.2.1
|
|
||||||
astunparse==1.6.3
|
|
||||||
async-lru==2.0.4
|
|
||||||
attrs==22.2.0
|
|
||||||
Babel==2.15.0
|
|
||||||
beautifulsoup4==4.12.3
|
|
||||||
better-exceptions==0.3.3
|
|
||||||
bleach==6.0.0
|
|
||||||
blinker==1.5
|
|
||||||
bottle==0.12.25
|
|
||||||
cachetools==5.3.0
|
|
||||||
CD==1.1.0
|
|
||||||
certifi==2022.12.7
|
|
||||||
cffi==1.16.0
|
|
||||||
chardet==5.1.0
|
|
||||||
charset-normalizer==3.0.1
|
|
||||||
click==8.1.3
|
|
||||||
colorama==0.4.6
|
|
||||||
comm==0.1.4
|
|
||||||
contourpy==1.0.7
|
|
||||||
cycler==0.11.0
|
|
||||||
dash==2.9.1
|
|
||||||
dash-bootstrap-components==1.4.1
|
|
||||||
dash-core-components==2.0.0
|
|
||||||
dash-html-components==2.0.0
|
|
||||||
dash-table==5.0.0
|
|
||||||
dateparser==1.1.8
|
|
||||||
debugpy==1.8.1
|
|
||||||
decorator==5.1.1
|
|
||||||
defusedxml==0.7.1
|
|
||||||
dill==0.3.7
|
|
||||||
dm-tree==0.1.8
|
|
||||||
entrypoints==0.4
|
|
||||||
exceptiongroup==1.1.3
|
|
||||||
exchange_calendars==4.5.5
|
|
||||||
executing==1.2.0
|
|
||||||
fastapi==0.109.2
|
|
||||||
fastjsonschema==2.19.1
|
|
||||||
filelock==3.13.1
|
|
||||||
Flask==2.2.3
|
|
||||||
flatbuffers==23.5.26
|
|
||||||
fonttools==4.39.0
|
|
||||||
fpdf2==2.7.6
|
|
||||||
fqdn==1.5.1
|
|
||||||
gast==0.4.0
|
|
||||||
gitdb==4.0.10
|
|
||||||
GitPython==3.1.31
|
|
||||||
google-auth==2.23.0
|
|
||||||
google-auth-oauthlib==1.0.0
|
|
||||||
google-pasta==0.2.0
|
|
||||||
greenlet==3.0.3
|
|
||||||
grpcio==1.58.0
|
|
||||||
h11==0.14.0
|
|
||||||
h5py==3.10.0
|
|
||||||
html2text==2024.2.26
|
|
||||||
httpcore==1.0.5
|
|
||||||
httpx==0.27.0
|
|
||||||
humanize==4.9.0
|
|
||||||
icecream==2.1.3
|
|
||||||
idna==3.4
|
|
||||||
imageio==2.31.6
|
|
||||||
importlib-metadata==6.1.0
|
|
||||||
ipykernel==6.29.4
|
|
||||||
ipython==8.17.2
|
|
||||||
ipywidgets==8.1.1
|
|
||||||
isoduration==20.11.0
|
|
||||||
itables==2.0.1
|
|
||||||
itsdangerous==2.1.2
|
|
||||||
jax==0.4.23
|
|
||||||
jaxlib==0.4.23
|
|
||||||
jedi==0.19.1
|
|
||||||
Jinja2==3.1.2
|
|
||||||
joblib==1.3.2
|
|
||||||
json5==0.9.25
|
|
||||||
jsonpointer==2.4
|
|
||||||
jsonschema==4.22.0
|
|
||||||
jsonschema-specifications==2023.12.1
|
|
||||||
jupyter-events==0.10.0
|
|
||||||
jupyter-lsp==2.2.5
|
|
||||||
jupyter_client==8.6.1
|
|
||||||
jupyter_core==5.7.2
|
|
||||||
jupyter_server==2.14.0
|
|
||||||
jupyter_server_terminals==0.5.3
|
|
||||||
jupyterlab==4.1.8
|
|
||||||
jupyterlab-widgets==3.0.9
|
|
||||||
jupyterlab_pygments==0.3.0
|
|
||||||
jupyterlab_server==2.27.1
|
|
||||||
kaleido==0.2.1
|
|
||||||
keras==3.0.2
|
|
||||||
keras-core==0.1.7
|
|
||||||
keras-nightly==3.0.3.dev2024010203
|
|
||||||
keras-nlp-nightly==0.7.0.dev2024010203
|
|
||||||
keras-tcn @ git+https://github.com/drew2323/keras-tcn.git@4bddb17a02cb2f31c9fe2e8f616b357b1ddb0e11
|
|
||||||
kiwisolver==1.4.4
|
|
||||||
korean-lunar-calendar==0.3.1
|
|
||||||
libclang==16.0.6
|
|
||||||
lightweight-charts @ git+https://github.com/drew2323/lightweight-charts-python.git@2b9f238a4242d958bc863b6209bf6444786477c5
|
|
||||||
llvmlite==0.39.1
|
|
||||||
Markdown==3.4.3
|
|
||||||
markdown-it-py==2.2.0
|
|
||||||
MarkupSafe==2.1.2
|
|
||||||
matplotlib==3.8.2
|
|
||||||
matplotlib-inline==0.1.6
|
|
||||||
mdurl==0.1.2
|
|
||||||
mistune==3.0.2
|
|
||||||
ml-dtypes==0.3.1
|
|
||||||
mlroom @ git+https://github.com/drew2323/mlroom.git@692900e274c4e0542d945d231645c270fc508437
|
|
||||||
mplfinance==0.12.10b0
|
|
||||||
msgpack==1.0.4
|
|
||||||
mypy-extensions==1.0.0
|
|
||||||
namex==0.0.7
|
|
||||||
nbclient==0.10.0
|
|
||||||
nbconvert==7.16.4
|
|
||||||
nbformat==5.10.4
|
|
||||||
nest-asyncio==1.6.0
|
|
||||||
newtulipy==0.4.6
|
|
||||||
notebook_shim==0.2.4
|
|
||||||
numba==0.56.4
|
|
||||||
numpy==1.23.5
|
|
||||||
oauthlib==3.2.2
|
|
||||||
opt-einsum==3.3.0
|
|
||||||
orjson==3.9.10
|
|
||||||
overrides==7.7.0
|
|
||||||
packaging==23.0
|
|
||||||
pandas==2.2.1
|
|
||||||
pandas_market_calendars==4.4.1
|
|
||||||
pandocfilters==1.5.1
|
|
||||||
param==1.13.0
|
|
||||||
parso==0.8.3
|
|
||||||
patsy==0.5.6
|
|
||||||
pexpect==4.8.0
|
|
||||||
Pillow==9.4.0
|
|
||||||
platformdirs==4.2.0
|
|
||||||
plotly==5.22.0
|
|
||||||
prometheus_client==0.20.0
|
|
||||||
prompt-toolkit==3.0.39
|
|
||||||
proto-plus==1.22.2
|
|
||||||
protobuf==3.20.3
|
|
||||||
proxy-tools==0.1.0
|
|
||||||
psutil==5.9.8
|
|
||||||
ptyprocess==0.7.0
|
|
||||||
pure-eval==0.2.2
|
|
||||||
pyarrow==11.0.0
|
|
||||||
pyasn1==0.4.8
|
|
||||||
pyasn1-modules==0.2.8
|
|
||||||
pycparser==2.22
|
|
||||||
pyct==0.5.0
|
|
||||||
pydantic==2.6.4
|
|
||||||
pydantic_core==2.16.3
|
|
||||||
pydeck==0.8.0
|
|
||||||
Pygments==2.14.0
|
|
||||||
pyinstrument==4.5.3
|
|
||||||
pyluach==2.2.0
|
|
||||||
Pympler==1.0.1
|
|
||||||
pyobjc-core==10.3
|
|
||||||
pyobjc-framework-Cocoa==10.3
|
|
||||||
pyobjc-framework-Security==10.3
|
|
||||||
pyobjc-framework-WebKit==10.3
|
|
||||||
pyparsing==3.0.9
|
|
||||||
pyrsistent==0.19.3
|
|
||||||
pysos==1.3.0
|
|
||||||
python-call-graph==2.1.2
|
|
||||||
python-dateutil==2.8.2
|
|
||||||
python-dotenv==1.0.0
|
|
||||||
python-json-logger==2.0.7
|
|
||||||
python-multipart==0.0.6
|
|
||||||
pytz==2022.7.1
|
|
||||||
pytz-deprecation-shim==0.1.0.post0
|
|
||||||
pyviz-comms==2.2.1
|
|
||||||
PyWavelets==1.5.0
|
|
||||||
pywebview==5.1
|
|
||||||
PyYAML==6.0
|
|
||||||
pyzmq==25.1.2
|
|
||||||
referencing==0.35.1
|
|
||||||
regex==2023.10.3
|
|
||||||
requests==2.31.0
|
|
||||||
requests-oauthlib==1.3.1
|
|
||||||
rfc3339-validator==0.1.4
|
|
||||||
rfc3986-validator==0.1.1
|
|
||||||
rich==13.3.1
|
|
||||||
rpds-py==0.18.0
|
|
||||||
rsa==4.9
|
|
||||||
schedule==1.2.1
|
|
||||||
scikit-learn==1.3.2
|
|
||||||
scipy==1.11.2
|
|
||||||
seaborn==0.12.2
|
|
||||||
semver==2.13.0
|
|
||||||
Send2Trash==1.8.3
|
|
||||||
six==1.16.0
|
|
||||||
smmap==5.0.0
|
|
||||||
sniffio==1.3.0
|
|
||||||
soupsieve==2.5
|
|
||||||
SQLAlchemy==2.0.27
|
|
||||||
sseclient-py==1.7.2
|
|
||||||
stack-data==0.6.3
|
|
||||||
starlette==0.36.3
|
|
||||||
statsmodels==0.14.1
|
|
||||||
streamlit==1.20.0
|
|
||||||
structlog==23.1.0
|
|
||||||
TA-Lib==0.4.28
|
|
||||||
tb-nightly==2.16.0a20240102
|
|
||||||
tenacity==8.2.2
|
|
||||||
tensorboard==2.15.1
|
|
||||||
tensorboard-data-server==0.7.1
|
|
||||||
tensorflow-addons==0.23.0
|
|
||||||
tensorflow-estimator==2.15.0
|
|
||||||
tensorflow-io-gcs-filesystem==0.34.0
|
|
||||||
termcolor==2.3.0
|
|
||||||
terminado==0.18.1
|
|
||||||
tf-estimator-nightly==2.14.0.dev2023080308
|
|
||||||
tf-nightly==2.16.0.dev20240101
|
|
||||||
tf_keras-nightly==2.16.0.dev2023123010
|
|
||||||
threadpoolctl==3.2.0
|
|
||||||
tinycss2==1.3.0
|
|
||||||
tinydb==4.7.1
|
|
||||||
tinydb-serialization==2.1.0
|
|
||||||
tinyflux==0.4.0
|
|
||||||
toml==0.10.2
|
|
||||||
tomli==2.0.1
|
|
||||||
toolz==0.12.0
|
|
||||||
tornado==6.2
|
|
||||||
tqdm==4.65.0
|
|
||||||
traitlets==5.13.0
|
|
||||||
typeguard==2.13.3
|
|
||||||
types-python-dateutil==2.9.0.20240316
|
|
||||||
typing_extensions==4.9.0
|
|
||||||
tzdata==2023.2
|
|
||||||
tzlocal==4.3
|
|
||||||
uri-template==1.3.0
|
|
||||||
urllib3==1.26.14
|
|
||||||
uvicorn==0.21.1
|
|
||||||
-e git+https://github.com/drew2323/v2trading.git@7a283a127e8b11914cb2ba0ad5ef551f5a398f62#egg=v2realbot
|
|
||||||
validators==0.20.0
|
|
||||||
vectorbtpro @ file:///Users/davidbrazda/Downloads/vectorbt.pro-develop
|
|
||||||
wcwidth==0.2.9
|
|
||||||
webcolors==1.13
|
|
||||||
webencodings==0.5.1
|
|
||||||
websocket-client==1.7.0
|
|
||||||
websockets==11.0.3
|
|
||||||
Werkzeug==2.2.3
|
|
||||||
widgetsnbextension==4.0.9
|
|
||||||
wrapt==1.14.1
|
|
||||||
zipp==3.15.0
|
|
||||||
Reference in New Issue
Block a user