5 Commits

Author SHA1 Message Date
30931f60d4 multiaccount first draft 2024-10-08 13:43:23 +02:00
79a033a633 changes 2024-08-26 12:54:06 +02:00
a10d5b8a64 treti commit 2024-08-25 18:06:47 +02:00
30044dc4ea druhy commit 2024-08-24 06:57:53 +02:00
30048364bb first commit 2024-08-23 14:16:51 +02:00
58 changed files with 4423 additions and 5044 deletions

File diff suppressed because one or more lines are too long

View File

@ -1691,7 +1691,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.10"
"version": "3.10.11"
}
},
"nbformat": 4,

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,932 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from v2realbot.tools.loadbatch import load_batch\n",
"from v2realbot.utils.utils import zoneNY\n",
"import pandas as pd\n",
"import numpy as np\n",
"import vectorbtpro as vbt\n",
"from itables import init_notebook_mode, show\n",
"import datetime\n",
"from itertools import product\n",
"from v2realbot.config import ACCOUNT1_PAPER_API_KEY, ACCOUNT1_PAPER_SECRET_KEY, DATA_DIR\n",
"\n",
"init_notebook_mode(all_interactive=True)\n",
"\n",
"vbt.settings.set_theme(\"dark\")\n",
"vbt.settings['plotting']['layout']['width'] = 1280\n",
"vbt.settings.plotting.auto_rangebreaks = True\n",
"# Set the option to display with pagination\n",
"pd.set_option('display.notebook_repr_html', True)\n",
"pd.set_option('display.max_rows', 10) # Number of rows per page\n",
"\n",
"# Define the market open and close times\n",
"market_open = datetime.time(9, 30)\n",
"market_close = datetime.time(16, 0)\n",
"entry_window_opens = 1\n",
"entry_window_closes = 370\n",
"\n",
"forced_exit_start = 380\n",
"forced_exit_end = 390\n",
"\n",
"#LOAD FROM PARQUET\n",
"#list all files is dir directory with parquet extension\n",
"dir = DATA_DIR + \"/notebooks/\"\n",
"import os\n",
"files = [f for f in os.listdir(dir) if f.endswith(\".parquet\")]\n",
"#print('\\n'.join(map(str, files)))\n",
"file_name = \"ohlcv_df-SPY-2024-01-01T09:30:00-2024-05-14T16:00:00.parquet\"\n",
"ohlcv_df = pd.read_parquet(dir+file_name,engine='pyarrow')\n",
"basic_data = vbt.Data.from_data(vbt.symbol_dict({\"SPY\": ohlcv_df}), tz_convert=zoneNY)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#parameters (primary y line, secondary y line, close)\n",
"def plot_2y_close(priminds, secinds, close):\n",
" fig = vbt.make_subplots(rows=1, cols=1, shared_xaxes=True, specs=[[{\"secondary_y\": True}]], vertical_spacing=0.02, subplot_titles=(\"MOM\", \"Price\" ))\n",
" close.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False), trace_kwargs=dict(line=dict(color=\"blue\")))\n",
" for ind in priminds:\n",
" ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False))\n",
" for ind in secinds:\n",
" ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True))\n",
" return fig\n",
"\n",
"# close = basic_data.xloc[\"09:30\":\"10:00\"].close"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#PIPELINE - FOR - LOOP\n",
"\n",
"#indicator parameters\n",
"mom_timeperiod = list(range(2, 12))\n",
"\n",
"#uzavreni okna od 1 do 200\n",
"#entry_window_closes = list(range(2, 50, 3))\n",
"entry_window_closes = [5, 10, 30, 45]\n",
"#entry_window_closes = 30\n",
"#threshold entries parameters\n",
"#long\n",
"mom_th = np.round(np.arange(0.01, 0.5 + 0.02, 0.02),4).tolist()#-0.02\n",
"# short\n",
"#mom_th = np.round(np.arange(-0.01, -0.3 - 0.02, -0.02),4).tolist()#-0.02\n",
"roc_th = np.round(np.arange(-0.2, -0.8 - 0.05, -0.05),4).tolist()#-0.2\n",
"#print(mom_th, roc_th)\n",
"\n",
"#portfolio simulation parameters\n",
"sl_stop =np.round(np.arange(0.02/100, 0.7/100, 0.05/100),4).tolist()\n",
"tp_stop = np.round(np.arange(0.02/100, 0.7/100, 0.05/100),4).tolist()\n",
"\n",
"combs = list(product(mom_timeperiod, mom_th, roc_th, sl_stop, tp_stop))\n",
"\n",
"@vbt.parameterized(merge_func = \"concat\", random_subset = 2000, show_progress=True) \n",
"def test_strat(entry_window_closes=60,\n",
" mom_timeperiod=2,\n",
" mom_th=-0.04,\n",
" #roc_th=-0.2,\n",
" sl_stop=0.19/100,\n",
" tp_stop=0.19/100):\n",
" # mom_timeperiod=2\n",
" # mom_th=-0.06\n",
" # roc_th=-0.2\n",
" # sl_stop=0.04/100\n",
" # tp_stop=0.04/100\n",
"\n",
" momshort = vbt.indicator(\"talib:MOM\").run(basic_data.close, timeperiod=mom_timeperiod, short_name = \"slope_short\")\n",
" rocp = vbt.indicator(\"talib:ROC\").run(basic_data.close, short_name = \"rocp\")\n",
" #rate of change + momentum\n",
"\n",
" #momshort.plot rocp.real_crossed_below(roc_th) & \n",
" #short_signal = momshort.real_crossed_below(mom_th)\n",
" long_signal = momshort.real_crossed_above(mom_th)\n",
" # print(\"short signal\")\n",
" # print(short_signal.value_counts())\n",
"\n",
" #forced_exit = pd.Series(False, index=close.index)\n",
" forced_exit = basic_data.symbol_wrapper.fill(False)\n",
" #entry_window_open = pd.Series(False, index=close.index)\n",
" entry_window_open= basic_data.symbol_wrapper.fill(False)\n",
"\n",
" #print(entry_window_closes, \"entry window closes\")\n",
" # Calculate the time difference in minutes from market open for each timestamp\n",
" elapsed_min_from_open = (forced_exit.index.hour - market_open.hour) * 60 + (forced_exit.index.minute - market_open.minute)\n",
"\n",
" entry_window_open[(elapsed_min_from_open >= entry_window_opens) & (elapsed_min_from_open < entry_window_closes)] = True\n",
"\n",
" #print(entry_window_open.value_counts())\n",
"\n",
" forced_exit[(elapsed_min_from_open >= forced_exit_start) & (elapsed_min_from_open < forced_exit_end)] = True\n",
" #short_entries = (short_signal & entry_window_open)\n",
" #short_exits = forced_exit\n",
" entries = (long_signal & entry_window_open)\n",
" exits = forced_exit\n",
" #long_entries.info()\n",
" #number of trues and falses in long_entries\n",
" #print(short_exits.value_counts())\n",
" #print(short_entries.value_counts())\n",
"\n",
" #fig = plot_2y_close([],[momshort, rocp], close)\n",
" #short_signal.vbt.signals.plot_as_entries(close, fig=fig, add_trace_kwargs=dict(secondary_y=False))\n",
" #print(sl_stop)\n",
" #tsl_th=sl_stop, \n",
" #short_entries=short_entries, short_exits=short_exits,\n",
" pf = vbt.Portfolio.from_signals(close=basic_data.close, entries=entries, exits=exits, tsl_stop=sl_stop, tp_stop = tp_stop, fees=0.0167/100, freq=\"1s\", price=\"close\") #sl_stop=sl_stop, tp_stop = sl_stop,\n",
" \n",
" return pf.stats([\n",
" 'total_return',\n",
" 'max_dd', \n",
" 'total_trades', \n",
" 'win_rate', \n",
" 'expectancy'\n",
" ])\n",
"\n",
"pf_results = test_strat(vbt.Param(entry_window_closes),\n",
" vbt.Param(mom_timeperiod),\n",
" vbt.Param(mom_th),\n",
" #vbt.Param(roc_th)\n",
" vbt.Param(sl_stop),\n",
" vbt.Param(tp_stop, condition=\"tp_stop > sl_stop\"))\n",
"pf_results = pf_results.unstack(level=-1)\n",
"pf_results.sort_values(by=[\"Total Return [%]\", \"Max Drawdown [%]\"], ascending=[False, True])\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#pf_results.load(\"10tiscomb.pickle\")\n",
"#pf_results.info()\n",
"\n",
"vbt.save(pf_results, \"8tiscomb_tsl.pickle\")\n",
"\n",
"# pf_results = vbt.load(\"8tiscomb_tsl.pickle\")\n",
"# pf_results\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# parallel_coordinates method¶\n",
"\n",
"# attach_px_methods.<locals>.plot_func(\n",
"# *args,\n",
"# layout=None,\n",
"# **kwargs\n",
"# )\n",
"\n",
"# pf_results.vbt.px.parallel_coordinates() #ocdf\n",
"\n",
"res = pf_results.reset_index()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf_results"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"from sklearn.decomposition import PCA\n",
"from sklearn.preprocessing import StandardScaler\n",
"import matplotlib.pyplot as plt\n",
"\n",
"# Assuming pf_results is your DataFrame\n",
"# Convert columns to numeric, assuming NaNs where conversion fails\n",
"metrics = ['Total Return [%]', 'Max Drawdown [%]', 'Total Trades']\n",
"for metric in metrics:\n",
" pf_results[metric] = pd.to_numeric(pf_results[metric], errors='coerce')\n",
"\n",
"# Handle missing values, for example filling with the median\n",
"pf_results['Max Drawdown [%]'].fillna(pf_results['Max Drawdown [%]'].median(), inplace=True)\n",
"\n",
"# Extract the metrics into a new DataFrame\n",
"data_for_pca = pf_results[metrics]\n",
"\n",
"# Standardize the data before applying PCA\n",
"scaler = StandardScaler()\n",
"data_scaled = scaler.fit_transform(data_for_pca)\n",
"\n",
"# Apply PCA\n",
"pca = PCA(n_components=2) # Adjust components as needed\n",
"principal_components = pca.fit_transform(data_scaled)\n",
"\n",
"# Create a DataFrame with the principal components\n",
"pca_results = pd.DataFrame(data=principal_components, columns=['PC1', 'PC2'])\n",
"\n",
"# Visualize the results\n",
"plt.figure(figsize=(8,6))\n",
"plt.scatter(pca_results['PC1'], pca_results['PC2'], alpha=0.5)\n",
"plt.xlabel('Principal Component 1')\n",
"plt.ylabel('Principal Component 2')\n",
"plt.title('PCA of Strategy Optimization Results')\n",
"plt.grid(True)\n",
"plt.savefig(\"ddd.png\")\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Check if there is any unnamed level and rename it\n",
"if None in df.index.names:\n",
" # Generate new names list replacing None with 'stat'\n",
" new_names = ['stat' if name is None else name for name in df.index.names]\n",
" df.index.set_names(new_names, inplace=True)\n",
"\n",
"rs= df\n",
"\n",
"rs.info()\n",
"\n",
"\n",
"# # Now, 'stat' is the name of the previously unnamed level\n",
"\n",
"# # Filter for 'Total Return' assuming it is a correct identifier in the 'stat' level\n",
"# total_return_series = df.xs('Total Return [%]', level='stat')\n",
"\n",
"# # Sort the Series to get the largest 'Total Return' values\n",
"# sorted_series = total_return_series.sort_values(ascending=False)\n",
"\n",
"# # Print the sorted filtered data\n",
"# sorted_series.head(20)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sorted_series.vbt.save()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#df.info()\n",
"total_return_series = df.xs('Total Return [%]')\n",
"sorted_series = total_return_series.sort_values(ascending=False)\n",
"\n",
"# Display the top N entries, e.g., top 5\n",
"sorted_series.head(5)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"df"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"comb_stats_df.nlargest(10, 'Total Return [%]')\n",
"#stats_df.info()\n",
"\n",
"\n",
"8\t-0.06\t-0.2\t0.0028\t0.0048\t4.156254\n",
"4 -0.02 -0.25 0.0028 0.0048 0.84433\n",
"3 -0.02 -0.25 0.0033 0.0023 Total Return [%] 0.846753\n",
"#2\t-0.04\t-0.2\t0.0019\t0.0019\n",
"# 2\t-0.04\t-0.2\t0.0019\t0.0019\t0.556919\t91\t60.43956\t0.00612\n",
"# 2\t-0.04\t-0.25\t0.0019\t0.0019\t0.556919\t91\t60.43956\t0.00612\n",
"# 2\t-0.04\t-0.3\t0.0019\t0.0019\t0.556919\t91\t60.43956\t0.00612\n",
"# 2\t-0.04\t-0.35\t0.0019\t0.0019\t0.556919\t91\t60.43956\t0.00612\n",
"# 2\t-0.04\t-0.4\t0.0019\t0.0019\t0.556919\t91\t60.43956\t0.00612\n",
"# 2\t-0.04\t-0.2\t0.0019\t0.0017\t0.451338\t93\t63.44086\t0.004853\n",
"# 2\t-0.04\t-0.25\t0.0019\t0.0017\t0.451338\t93\t63.44086\t0.004853\n",
"# 2\t-0.04\t-0.3\t0.0019\t0.0017\t0.451338\t93\t63.44086\t0.004853\n",
"# 2\t-0.04\t-0.35\t0.0019\t0.0017\t0.451338\t93\t63.44086\t0.004853\n",
"# 2\t-0.04\t-0.4\t0.0019\t0.0017\t0.451338\t93\t63.44086\t0.004853"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"basic_data.symbols"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
">>> def apply_func(ts, entries, exits, fastw, sloww, minp=None):\n",
"... fast_ma = vbt.nb.rolling_mean_nb(ts, fastw, minp=minp)\n",
"... slow_ma = vbt.nb.rolling_mean_nb(ts, sloww, minp=minp)\n",
"... entries[:] = vbt.nb.crossed_above_nb(fast_ma, slow_ma) \n",
"... exits[:] = vbt.nb.crossed_above_nb(slow_ma, fast_ma)\n",
"... return (fast_ma, slow_ma) \n",
"\n",
">>> CrossSig = vbt.IF(\n",
"... class_name=\"CrossSig\",\n",
"... input_names=['ts'],\n",
"... in_output_names=['entries', 'exits'],\n",
"... param_names=['fastw', 'sloww'],\n",
"... output_names=['fast_ma', 'slow_ma']\n",
"... ).with_apply_func(\n",
"... apply_func,\n",
"... in_output_settings=dict(\n",
"... entries=dict(dtype=np.bool_), #initialize output with bool\n",
"... exits=dict(dtype=np.bool_)\n",
"... )\n",
"... )\n",
">>> cross_sig = CrossSig.run(ts2, 2, 4)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#PIPELINE - parameters in one go\n",
"\n",
"\n",
"#TOTO prepsat do FOR-LOOPu\n",
"\n",
"\n",
"#indicator parameters\n",
"mom_timeperiod = list(range(2, 6))\n",
"\n",
"#threshold entries parameters\n",
"mom_th = np.round(np.arange(-0.02, -0.1 - 0.02, -0.02),4).tolist()#-0.02\n",
"roc_th = np.round(np.arange(-0.2, -0.4 - 0.05, -0.05),4).tolist()#-0.2\n",
"#print(mom_th, roc_th)\n",
"#jejich product\n",
"# mom_th_prod, roc_th_prod = zip(*product(mom_th, roc_th))\n",
"\n",
"# #convert threshold to vbt param\n",
"# mom_th_index = vbt.Param(mom_th_prod, name='mom_th_th') \n",
"# roc_th_index = vbt.Param(roc_th_prod, name='roc_th_th')\n",
"\n",
"mom_th = vbt.Param(mom_th, name='mom_th')\n",
"roc_th = vbt.Param(roc_th, name='roc_th')\n",
"\n",
"#portfolio simulation parameters\n",
"sl_stop = np.arange(0.03/100, 0.2/100, 0.02/100).tolist()\n",
"# Using the round function\n",
"sl_stop = [round(val, 4) for val in sl_stop]\n",
"tp_stop = np.arange(0.03/100, 0.2/100, 0.02/100).tolist()\n",
"# Using the round function\n",
"tp_stop = [round(val, 4) for val in tp_stop]\n",
"sl_stop = vbt.Param(sl_stop) #np.nan mean s no stoploss\n",
"tp_stop = vbt.Param(tp_stop) #np.nan mean s no stoploss\n",
"\n",
"\n",
"#def test_mom(window=14, mom_th=0.2, roc_th=0.2, sl_stop=0.03/100, tp_stop=0.03/100):\n",
"#close = basic_data.xloc[\"09:30\":\"10:00\"].close\n",
"momshort = vbt.indicator(\"talib:MOM\").run(basic_data.get(\"Close\"), timeperiod=mom_timeperiod, short_name = \"slope_short\")\n",
"\n",
"#ht_trendline = vbt.indicator(\"talib:HT_TRENDLINE\").run(close, short_name = \"httrendline\")\n",
"rocp = vbt.indicator(\"talib:ROC\").run(basic_data.get(\"Close\"), short_name = \"rocp\")\n",
"#rate of change + momentum\n",
"\n",
"rocp_signal = rocp.real_crossed_below(mom_th)\n",
"mom_signal = momshort.real_crossed_below(roc_th)\n",
"\n",
"#mom_signal\n",
"print(rocp_signal.info())\n",
"print(mom_signal.info())\n",
"#print(rocp.real)\n",
"\n",
"\n",
"short_signal = (mom_signal.vbt & rocp_signal)\n",
"\n",
"# #short_signal = (rocp.real_crossed_below(roc_th_index) & momshort.real_crossed_below(mom_th_index))\n",
"# forced_exit = m1_data.symbol_wrapper.fill(False)\n",
"# entry_window_open= m1_data.symbol_wrapper.fill(False)\n",
"\n",
"\n",
"# # Calculate the time difference in minutes from market open for each timestamp\n",
"# elapsed_min_from_open = (forced_exit.index.hour - market_open.hour) * 60 + (forced_exit.index.minute - market_open.minute)\n",
"\n",
"# entry_window_open[(elapsed_min_from_open >= entry_window_opens) & (elapsed_min_from_open < entry_window_closes)] = True\n",
"# forced_exit[(elapsed_min_from_open >= forced_exit_start) & (elapsed_min_from_open < forced_exit_end)] = True\n",
"# short_entries = (short_signal & entry_window_open)\n",
"# short_exits = forced_exit\n",
"# #long_entries.info()\n",
"# #number of trues and falses in long_entries\n",
"# #short_exits.value_counts()\n",
"# #short_entries.value_counts()\n",
"\n",
"\n",
"# pf = vbt.Portfolio.from_signals(close=close, short_entries=short_entries, short_exits=short_exits, sl_stop=sl_stop, tp_stop = tp_stop, fees=0.0167/100, freq=\"1s\") #sl_stop=sl_stop, tp_stop = sl_stop,\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# filter dates"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#filter na dny\n",
"dates_of_interest = pd.to_datetime(['2024-04-22']).tz_localize('US/Eastern')\n",
"filtered_df = df.loc[df.index.normalize().isin(dates_of_interest)]\n",
"\n",
"df = filtered_df\n",
"df.info()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# import plotly.io as pio\n",
"# pio.renderers.default = 'notebook'\n",
"\n",
"#naloadujeme do vbt symbol as column\n",
"basic_data = vbt.Data.from_data({\"BAC\": df}, tz_convert=zoneNY)\n",
"\n",
"vbt.settings.plotting.auto_rangebreaks = True\n",
"#basic_data.data[\"BAC\"].vbt.ohlcv.plot()\n",
"\n",
"#basic_data.data[\"BAC\"]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"m1_data = basic_data[['Open', 'High', 'Low', 'Close', 'Volume']]\n",
"\n",
"m1_data.data[\"BAC\"]\n",
"#m5_data = m1_data.resample(\"5T\")\n",
"\n",
"#m5_data.data[\"BAC\"].head(10)\n",
"\n",
"# m15_data = m1_data.resample(\"15T\")\n",
"\n",
"# m15 = m15_data.data[\"BAC\"]\n",
"\n",
"# m15.vbt.ohlcv.plot()\n",
"\n",
"# m1_data.wrapper.index\n",
"\n",
"# m1_resampler = m1_data.wrapper.get_resampler(\"1T\")\n",
"# m1_resampler.index_difference(reverse=True)\n",
"\n",
"\n",
"# m5_resampler.prettify()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# MOM indicator"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"vbt.phelp(vbt.indicator(\"talib:ROCP\").run)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"vyuzití rychleho klesani na sekundove urovni behem open rush\n",
"- MOM + ROC during open rush\n",
"- short signal\n",
"- pipeline kombinace thresholdu pro vstup mom_th, roc_th + hodnota sl_stop a tp_stop (pripadne trailing) - nalezeni optimalni kombinace atributu"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# fig = plot_2y_close([ht_trendline],[momshort, rocp], close)\n",
"# short_signal.vbt.signals.plot_as_entries(close, fig=fig, add_trace_kwargs=dict(secondary_y=False)) \n",
"\n",
"#parameters (primary y line, secondary y line, close)\n",
"def plot_2y_close(priminds, secinds, close):\n",
" fig = vbt.make_subplots(rows=1, cols=1, shared_xaxes=True, specs=[[{\"secondary_y\": True}]], vertical_spacing=0.02, subplot_titles=(\"MOM\", \"Price\" ))\n",
" close.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False), trace_kwargs=dict(line=dict(color=\"blue\")))\n",
" for ind in priminds:\n",
" ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False))\n",
" for ind in secinds:\n",
" ind.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True))\n",
" return fig\n",
"\n",
"close = m1_data.xloc[\"09:30\":\"10:00\"].close\n",
"momshort = vbt.indicator(\"talib:MOM\").run(close, timeperiod=3, short_name = \"slope_short\")\n",
"ht_trendline = vbt.indicator(\"talib:HT_TRENDLINE\").run(close, short_name = \"httrendline\")\n",
"rocp = vbt.indicator(\"talib:ROC\").run(close, short_name = \"rocp\")\n",
"#rate of change + momentum\n",
"short_signal = (rocp.real_crossed_below(-0.2) & momshort.real_crossed_below(-0.02))\n",
"#indlong = vbt.indicator(\"talib:MOM\").run(close, timeperiod=10, short_name = \"slope_long\")\n",
"fig = plot_2y_close([ht_trendline],[momshort, rocp], close)\n",
"short_signal.vbt.signals.plot_as_entries(close, fig=fig, add_trace_kwargs=dict(secondary_y=False)) "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"close = m1_data.close\n",
"#vbt.phelp(vbt.OLS.run)\n",
"\n",
"#oer steepmnes of regression line\n",
"#talib.LINEARREG_SLOPE(close, timeperiod=timeperiod)\n",
"#a také ON BALANCE VOLUME - http://5.161.179.223:8000/static/js/vbt/api/indicators/custom/obv/index.html\n",
"\n",
"\n",
"\n",
"mom_ind = vbt.indicator(\"talib:MOM\") \n",
"#vbt.phelp(mom_ind.run)\n",
"\n",
"mom = mom_ind.run(close, timeperiod=10)\n",
"\n",
"plot_2y_close(mom, close)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# defining ENTRY WINDOW and forced EXIT window"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#m1_data.data[\"BAC\"].info()\n",
"import datetime\n",
"# Define the market open and close times\n",
"market_open = datetime.time(9, 30)\n",
"market_close = datetime.time(16, 0)\n",
"entry_window_opens = 2\n",
"entry_window_closes = 30\n",
"\n",
"forced_exit_start = 380\n",
"forced_exit_end = 390\n",
"\n",
"forced_exit = m1_data.symbol_wrapper.fill(False)\n",
"entry_window_open= m1_data.symbol_wrapper.fill(False)\n",
"\n",
"# Calculate the time difference in minutes from market open for each timestamp\n",
"elapsed_min_from_open = (forced_exit.index.hour - market_open.hour) * 60 + (forced_exit.index.minute - market_open.minute)\n",
"\n",
"entry_window_open[(elapsed_min_from_open >= entry_window_opens) & (elapsed_min_from_open < entry_window_closes)] = True\n",
"forced_exit[(elapsed_min_from_open >= forced_exit_start) & (elapsed_min_from_open < forced_exit_end)] = True\n",
"\n",
"#entry_window_open.info()\n",
"# forced_exit.tail(100)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"close = m1_data.close\n",
"\n",
"#rsi = vbt.RSI.run(close, window=14)\n",
"\n",
"short_entries = (short_signal & entry_window_open)\n",
"short_exits = forced_exit\n",
"#long_entries.info()\n",
"#number of trues and falses in long_entries\n",
"#short_exits.value_counts()\n",
"short_entries.value_counts()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def plot_rsi(close, entries, exits):\n",
" fig = vbt.make_subplots(rows=1, cols=1, shared_xaxes=True, specs=[[{\"secondary_y\": True}]], vertical_spacing=0.02, subplot_titles=(\"RSI\", \"Price\" ))\n",
" close.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True))\n",
" #rsi.plot(fig=fig, add_trace_kwargs=dict(secondary_y=False))\n",
" entries.vbt.signals.plot_as_entries(close, fig=fig, add_trace_kwargs=dict(secondary_y=False)) \n",
" exits.vbt.signals.plot_as_exits(close, fig=fig, add_trace_kwargs=dict(secondary_y=False)) \n",
" return fig\n",
"\n",
"plot_rsi(close, short_entries, short_exits)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"vbt.phelp(vbt.Portfolio.from_signals)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sl_stop = np.arange(0.03/100, 0.2/100, 0.02/100).tolist()\n",
"# Using the round function\n",
"sl_stop = [round(val, 4) for val in sl_stop]\n",
"print(sl_stop)\n",
"sl_stop = vbt.Param(sl_stop) #np.nan mean s no stoploss\n",
"\n",
"pf = vbt.Portfolio.from_signals(close=close, short_entries=short_entries, short_exits=short_exits, sl_stop=0.03/100, tp_stop = 0.03/100, fees=0.0167/100, freq=\"1s\") #sl_stop=sl_stop, tp_stop = sl_stop,\n",
"\n",
"#pf.stats()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#list of orders\n",
"#pf.orders.records_readable\n",
"#pf.orders.plots()\n",
"#pf.stats()\n",
"pf.stats()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf[(0.0015,0.0013)].plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf[0.03].plot_trade_signals()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# pristup k pf jako multi index"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#pf[0.03].plot()\n",
"#pf.order_records\n",
"pf[(0.03)].stats()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#zgrupovane statistiky\n",
"stats_df = pf.stats([\n",
" 'total_return',\n",
" 'total_trades',\n",
" 'win_rate',\n",
" 'expectancy'\n",
"], agg_func=None)\n",
"stats_df\n",
"\n",
"\n",
"stats_df.nlargest(10, 'Total Return [%]')\n",
"#stats_df.info()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf[(0.0011,0.0013000000000000002)].plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from pandas.tseries.offsets import DateOffset\n",
"\n",
"temp_data = basic_data['2024-4-22']\n",
"temp_data\n",
"res1m = temp_data[[\"Open\", \"High\", \"Low\", \"Close\", \"Volume\"]]\n",
"\n",
"# Define a custom date offset that starts at 9:30 AM and spans 4 hours\n",
"custom_offset = DateOffset(hours=4, minutes=30)\n",
"\n",
"# res1m = res1m.get().resample(\"4H\").agg({ \n",
"# \"Open\": \"first\",\n",
"# \"High\": \"max\",\n",
"# \"Low\": \"min\",\n",
"# \"Close\": \"last\",\n",
"# \"Volume\": \"sum\"\n",
"# })\n",
"\n",
"res4h = res1m.resample(\"1h\", resample_kwargs=dict(origin=\"start\"))\n",
"\n",
"res4h.data\n",
"\n",
"res15m = res1m.resample(\"15T\", resample_kwargs=dict(origin=\"start\"))\n",
"\n",
"res15m.data[\"BAC\"]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"@vbt.njit\n",
"def long_entry_place_func_nb(c, low, close, time_in_ns, rsi14, window_open, window_close):\n",
" market_open_minutes = 570 # 9 hours * 60 minutes + 30 minutes\n",
"\n",
" for out_i in range(len(c.out)):\n",
" i = c.from_i + out_i\n",
"\n",
" current_minutes = vbt.dt_nb.hour_nb(time_in_ns[i]) * 60 + vbt.dt_nb.minute_nb(time_in_ns[i])\n",
" #print(\"current_minutes\", current_minutes)\n",
" # Calculate elapsed minutes since market open at 9:30 AM\n",
" elapsed_from_open = current_minutes - market_open_minutes\n",
" elapsed_from_open = elapsed_from_open if elapsed_from_open >= 0 else 0\n",
" #print( \"elapsed_from_open\", elapsed_from_open)\n",
"\n",
" #elapsed_from_open = elapsed_minutes_from_open_nb(time_in_ns) \n",
" in_window = elapsed_from_open > window_open and elapsed_from_open < window_close\n",
" #print(\"in_window\", in_window)\n",
" # if in_window:\n",
" # print(\"in window\")\n",
"\n",
" if in_window and rsi14[i] > 60: # and low[i, c.col] <= hit_price: # and hour == 9: # (4)!\n",
" return out_i\n",
" return -1\n",
"\n",
"@vbt.njit\n",
"def long_exit_place_func_nb(c, high, close, time_index, tp, sl): # (5)!\n",
" entry_i = c.from_i - c.wait\n",
" entry_price = close[entry_i, c.col]\n",
" hit_price = entry_price * (1 + tp)\n",
" stop_price = entry_price * (1 - sl)\n",
" for out_i in range(len(c.out)):\n",
" i = c.from_i + out_i\n",
" last_bar_of_day = vbt.dt_nb.day_changed_nb(time_index[i], time_index[i + 1])\n",
"\n",
" #print(next_day)\n",
" if last_bar_of_day: #pokud je dalsi next day, tak zavirame posledni\n",
" print(\"ted\",out_i)\n",
" return out_i\n",
" if close[i, c.col] >= hit_price or close[i, c.col] <= stop_price :\n",
" return out_i\n",
" return -1\n",
"\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"df = pd.DataFrame(np.random.random(size=(5, 10)), columns=list('abcdefghij'))\n",
"\n",
"df"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"df.sum()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.11"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -0,0 +1,782 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# SUPERTREND\n",
"\n",
"* kombinace supertrendu na vice urovnich"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"from dotenv import load_dotenv\n",
"\n",
"#as V2realbot is client , load env variables here\n",
"env_file = \"/Users/davidbrazda/Documents/Development/python/.env\"\n",
"# Load the .env file\n",
"load_dotenv(env_file)\n",
"\n",
"from v2realbot.utils.utils import zoneNY\n",
"import pandas as pd\n",
"import numpy as np\n",
"import vectorbtpro as vbt\n",
"# from itables import init_notebook_mode, show\n",
"import datetime\n",
"from itertools import product\n",
"from v2realbot.config import DATA_DIR\n",
"from lightweight_charts import JupyterChart, chart, Panel, PlotAccessor\n",
"from IPython.display import display\n",
"\n",
"# init_notebook_mode(all_interactive=True)\n",
"\n",
"vbt.settings.set_theme(\"dark\")\n",
"vbt.settings['plotting']['layout']['width'] = 1280\n",
"vbt.settings.plotting.auto_rangebreaks = True\n",
"# Set the option to display with pagination\n",
"pd.set_option('display.notebook_repr_html', True)\n",
"pd.set_option('display.max_rows', 10) # Number of rows per page"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"trades_df-BAC-2024-01-01T09_30_00-2024-05-14T16_00_00-CO4B7VPWUZF-100.parquet\n",
"trades_df-BAC-2024-01-11T09:30:00-2024-01-12T16:00:00.parquet\n",
"trades_df-SPY-2024-01-01T09:30:00-2024-05-14T16:00:00.parquet\n",
"trades_df-BAC-2023-01-01T09_30_00-2024-05-25T16_00_00-47BCFOPUVWZ-100.parquet\n",
"ohlcv_df-BAC-2024-01-11T09:30:00-2024-01-12T16:00:00.parquet\n",
"trades_df-BAC-2024-05-15T09_30_00-2024-05-25T16_00_00-47BCFOPUVWZ-100.parquet\n",
"ohlcv_df-BAC-2024-01-01T09_30_00-2024-05-25T16_00_00-47BCFOPUVWZ-100.parquet\n",
"ohlcv_df-SPY-2024-01-01T09:30:00-2024-05-14T16:00:00.parquet\n",
"ohlcv_df-BAC-2024-01-01T09_30_00-2024-05-14T16_00_00-CO4B7VPWUZF-100.parquet\n",
"ohlcv_df-BAC-2023-01-01T09_30_00-2024-05-25T16_00_00-47BCFOPUVWZ-100.parquet\n",
"ohlcv_df-BAC-2023-01-01T09_30_00-2024-05-25T15_30_00-47BCFOPUVWZ-100.parquet\n"
]
},
{
"data": {
"text/plain": [
"351"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Define the market open and close times\n",
"market_open = datetime.time(9, 30)\n",
"market_close = datetime.time(16, 0)\n",
"entry_window_opens = 1\n",
"entry_window_closes = 370\n",
"forced_exit_start = 380\n",
"forced_exit_end = 390\n",
"\n",
"#LOAD FROM PARQUET\n",
"#list all files is dir directory with parquet extension\n",
"dir = DATA_DIR + \"/notebooks/\"\n",
"import os\n",
"files = [f for f in os.listdir(dir) if f.endswith(\".parquet\")]\n",
"print('\\n'.join(map(str, files)))\n",
"file_name = \"ohlcv_df-BAC-2023-01-01T09_30_00-2024-05-25T15_30_00-47BCFOPUVWZ-100.parquet\"\n",
"ohlcv_df = pd.read_parquet(dir+file_name,engine='pyarrow')\n",
"#filter ohlcv_df to certain date range (assuming datetime index)\n",
"#ohlcv_df = ohlcv_df.loc[\"2024-02-12 9:30\":\"2024-02-14 16:00\"]\n",
"\n",
"#add vwap column to ohlcv_df\n",
"#ohlcv_df[\"hlcc4\"] = (ohlcv_df[\"close\"] + ohlcv_df[\"high\"] + ohlcv_df[\"low\"] + ohlcv_df[\"close\"]) / 4\n",
"\n",
"basic_data = vbt.Data.from_data(vbt.symbol_dict({\"BAC\": ohlcv_df}), tz_convert=zoneNY)\n",
"ohlcv_df= None\n",
"basic_data.wrapper.index.normalize().nunique()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<class 'pandas.core.frame.DataFrame'>\n",
"DatetimeIndex: 4549772 entries, 2023-01-03 09:30:01-05:00 to 2024-05-24 15:59:59-04:00\n",
"Data columns (total 10 columns):\n",
" # Column Dtype \n",
"--- ------ ----- \n",
" 0 open float64 \n",
" 1 high float64 \n",
" 2 low float64 \n",
" 3 close float64 \n",
" 4 volume float64 \n",
" 5 trades float64 \n",
" 6 updated datetime64[ns, US/Eastern]\n",
" 7 vwap float64 \n",
" 8 buyvolume float64 \n",
" 9 sellvolume float64 \n",
"dtypes: datetime64[ns, US/Eastern](1), float64(9)\n",
"memory usage: 381.8 MB\n"
]
}
],
"source": [
"basic_data.data[\"BAC\"].info()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Add resample function to custom columns"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"from vectorbtpro.utils.config import merge_dicts, Config, HybridConfig\n",
"from vectorbtpro import _typing as tp\n",
"from vectorbtpro.generic import nb as generic_nb\n",
"\n",
"_feature_config: tp.ClassVar[Config] = HybridConfig(\n",
" {\n",
" \"buyvolume\": dict(\n",
" resample_func=lambda self, obj, resampler: obj.vbt.resample_apply(\n",
" resampler,\n",
" generic_nb.sum_reduce_nb,\n",
" )\n",
" ),\n",
" \"sellvolume\": dict(\n",
" resample_func=lambda self, obj, resampler: obj.vbt.resample_apply(\n",
" resampler,\n",
" generic_nb.sum_reduce_nb,\n",
" )\n",
" ),\n",
" \"trades\": dict(\n",
" resample_func=lambda self, obj, resampler: obj.vbt.resample_apply(\n",
" resampler,\n",
" generic_nb.sum_reduce_nb,\n",
" )\n",
" )\n",
" }\n",
")\n",
"\n",
"basic_data._feature_config = _feature_config"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"s1data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','trades','sellvolume']]\n",
"\n",
"s5data = s1data.resample(\"5s\")\n",
"s5data = s5data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"\n",
"t1data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','trades','sellvolume']].resample(\"1T\")\n",
"t1data = t1data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"# t1data.data[\"BAC\"].info()\n",
"\n",
"t30data = basic_data[['open', 'high', 'low', 'close', 'volume','vwap','buyvolume','trades','sellvolume']].resample(\"30T\")\n",
"t30data = t30data.transform(lambda df: df.between_time('09:30', '16:00').dropna())\n",
"# t30data.data[\"BAC\"].info()\n",
"\n",
"s1close = s1data.close\n",
"t1close = t1data.close\n",
"t30close = t30data.close\n",
"t30volume = t30data.volume\n",
"\n",
"#resample on specific index \n",
"resampler = vbt.Resampler(t30data.index, s1data.index, source_freq=\"30T\", target_freq=\"1s\")\n",
"t30close_realigned = t30close.vbt.realign_closing(resampler)\n",
"\n",
"#resample 1min to s\n",
"resampler_s = vbt.Resampler(t1data.index, s1data.index, source_freq=\"1T\", target_freq=\"1s\")\n",
"t1close_realigned = t1close.vbt.realign_closing(resampler_s)"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"datetime64[ns, US/Eastern]\n",
"datetime64[ns, US/Eastern]\n"
]
}
],
"source": [
"print(t30data.index.dtype)\n",
"print(s1data.index.dtype)"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<class 'pandas.core.frame.DataFrame'>\n",
"DatetimeIndex: 4551 entries, 2023-01-03 09:30:00-05:00 to 2024-05-24 15:30:00-04:00\n",
"Data columns (total 9 columns):\n",
" # Column Non-Null Count Dtype \n",
"--- ------ -------------- ----- \n",
" 0 open 4551 non-null float64\n",
" 1 high 4551 non-null float64\n",
" 2 low 4551 non-null float64\n",
" 3 close 4551 non-null float64\n",
" 4 volume 4551 non-null float64\n",
" 5 vwap 4551 non-null float64\n",
" 6 buyvolume 4551 non-null float64\n",
" 7 trades 4551 non-null float64\n",
" 8 sellvolume 4551 non-null float64\n",
"dtypes: float64(9)\n",
"memory usage: 355.5 KB\n"
]
}
],
"source": [
"t30data.data[\"BAC\"].info()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"vbt.IF.list_indicators(\"*vwap\")\n",
"vbt.phelp(vbt.VWAP.run)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# VWAP"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"t1vwap_h = vbt.VWAP.run(t1data.high, t1data.low, t1data.close, t1data.volume, anchor=\"H\")\n",
"t1vwap_d = vbt.VWAP.run(t1data.high, t1data.low, t1data.close, t1data.volume, anchor=\"D\")\n",
"t1vwap_t = vbt.VWAP.run(t1data.high, t1data.low, t1data.close, t1data.volume, anchor=\"T\")\n",
"\n",
"t1vwap_h_real = t1vwap_h.vwap.vbt.realign_closing(resampler_s)\n",
"t1vwap_d_real = t1vwap_d.vwap.vbt.realign_closing(resampler_s)\n",
"t1vwap_t_real = t1vwap_t.vwap.vbt.realign_closing(resampler_s)\n",
"\n",
"#t1vwap_5t.xloc[\"2024-01-3 09:30:00\":\"2024-01-03 16:00:00\"].plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#m30data.close.lw.plot()\n",
"#quick few liner\n",
"pane1 = Panel(\n",
" histogram=[\n",
" #(s1data.volume, \"volume\",None, 0.8),\n",
" #(m30volume, \"m30volume\",None, 1)\n",
" ], # [(series, name, \"rgba(53, 94, 59, 0.6)\", opacity)]\n",
" right=[\n",
" (s1data.close, \"1s close\"),\n",
" (t1data.close, \"1min close\"),\n",
" (t1vwap_t, \"1mvwap_t\"),\n",
" (t1vwap_h, \"1mvwap_h\"),\n",
" (t1vwap_d, \"1mvwap_d\"),\n",
" (t1vwap_t_real, \"1mvwap_t_real\"),\n",
" (t1vwap_h_real, \"1mvwap_h_real\"),\n",
" (t1vwap_d_real, \"1mvwap_d_real\")\n",
" # (t1close_realigned, \"1min close realigned\"),\n",
" # (m30data.close, \"30min-close\"),\n",
" # (m30close_realigned, \"30min close realigned\"),\n",
" ],\n",
")\n",
"ch = chart([pane1], size=\"s\", xloc=slice(\"2024-05-1 09:30:00\",\"2024-05-25 16:00:00\"))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# SUPERTREND"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"supertrend_s1 = vbt.SUPERTREND.run(s1data.high, s1data.low, s1data.close, period=5, multiplier=3)\n",
"direction_series_s1 = supertrend_s1.direction\n",
"supertrend_t1 = vbt.SUPERTREND.run(t1data.high, t1data.low, t1data.close, period=14, multiplier=3)\n",
"direction_series_t1 = supertrend_t1.direction\n",
"supertrend_t30 = vbt.SUPERTREND.run(t30data.high, t30data.low, t30data.close, period=14, multiplier=3)\n",
"direction_series_t30 = supertrend_t30.direction\n",
"\n",
"resampler_1t_sec = vbt.Resampler(direction_series_t1.index, direction_series_s1.index, source_freq=\"1T\", target_freq=\"1s\")\n",
"resampler_30t_sec = vbt.Resampler(direction_series_t30.index, direction_series_s1.index, source_freq=\"30T\", target_freq=\"1s\")\n",
"direction_series_t1_realigned = direction_series_t1.vbt.realign_closing(resampler_1t_sec)\n",
"direction_series_t30_realigned = direction_series_t30.vbt.realign_closing(resampler_30t_sec)\n",
"\n",
"#supertrend_s1.xloc[\"2024-01-3 09:30:00\":\"2024-01-03 16:00:00\"].plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# aligned_ups= pd.Series(False, index=direction_real.index)\n",
"# aligned_downs= pd.Series(False, index=direction_real.index)\n",
"\n",
"# aligned_ups = direction_real == 1 & supertrend.direction == 1\n",
"# aligned_ups"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s5close = s5data.data[\"BAC\"].close\n",
"s5open = s5data.data[\"BAC\"].open\n",
"s5high = s5data.data[\"BAC\"].high\n",
"s5close_prev = s5close.shift(1)\n",
"s5open_prev = s5open.shift(1)\n",
"s5high_prev = s5high.shift(1)\n",
"#gap nahoru od byci svicky a nevraci se zpet na jeji uroven\n",
"entry_ups = (s5close_prev > s5open_prev) & (s5open > s5high_prev + 0.010) & (s5close > s5close_prev)\n",
"\n",
"entry_ups.value_counts()\n",
"\n",
"#entry_ups.info()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Entry window"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"market_open = datetime.time(9, 30)\n",
"market_close = datetime.time(16, 0)\n",
"entry_window_opens = 10\n",
"entry_window_closes = 370"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"entry_window_open= pd.Series(False, index=entry_ups.index)\n",
"# Calculate the time difference in minutes from market open for each timestamp\n",
"elapsed_min_from_open = (entry_ups.index.hour - market_open.hour) * 60 + (entry_ups.index.minute - market_open.minute)\n",
"entry_window_open[(elapsed_min_from_open >= entry_window_opens) & (elapsed_min_from_open < entry_window_closes)] = True\n",
"#entry_window_open\n",
"\n",
"entry_ups = entry_ups & entry_window_open\n",
"# entry_ups\n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s5vwap_h = vbt.VWAP.run(s5data.high, s5data.low, s5data.close, s5data.volume, anchor=\"H\")\n",
"s5vwap_d = vbt.VWAP.run(s5data.high, s5data.low, s5data.close, s5data.volume, anchor=\"D\")\n",
"\n",
"# s5vwap_h_real = s5vwap_h.vwap.vbt.realign_closing(resampler_s)\n",
"# s5vwap_d_real = s5vwap_d.vwap.vbt.realign_closing(resampler_s)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pane1 = Panel(\n",
" ohlcv=(s5data.data[\"BAC\"],), #(series, entries, exits, other_markers)\n",
" histogram=[], # [(series, name, \"rgba(53, 94, 59, 0.6), opacity\")]\n",
" right=[#(bbands,), #[(series, name, entries, exits, other_markers)]\n",
" (s5data.data[\"BAC\"].close, \"close\", entry_ups),\n",
" (s5data.data[\"BAC\"].open, \"open\"),\n",
" (s5vwap_h, \"vwap5s_H\",),\n",
" (s5vwap_d, \"vwap5s_D\",)\n",
" # (t1data.data[\"BAC\"].vwap, \"vwap\"),\n",
" # (t1data.close, \"1min close\"),\n",
" # (supertrend_s1.trend,\"STtrend\"),\n",
" # (supertrend_s1.long,\"STlong\"),\n",
" # (supertrend_s1.short,\"STshort\")\n",
" ],\n",
" left = [\n",
" #(direction_series_s1,\"direction_s1\"),\n",
" # (direction_series_t1,\"direction_t1\"),\n",
" # (direction_series_t30,\"direction_t30\")\n",
" \n",
" ],\n",
" # right=[(bbands.upperband, \"upperband\",),\n",
" # (bbands.lowerband, \"lowerband\",),\n",
" # (bbands.middleband, \"middleband\",)\n",
" # ], #[(series, name, entries, exits, other_markers)]\n",
" middle1=[],\n",
" middle2=[],\n",
")\n",
"\n",
"# pane2 = Panel(\n",
"# ohlcv=(t1data.data[\"BAC\"],uptrend_m30, downtrend_m30), #(series, entries, exits, other_markers)\n",
"# histogram=[], # [(series, name, \"rgba(53, 94, 59, 0.6), opacity\")]\n",
"# left=[#(bbands,), #[(series, name, entries, exits, other_markers)]\n",
"# (direction_real,\"direction30min_real\"),\n",
"# ],\n",
"# # left = [(supertrendm30.direction,\"STdirection30\")],\n",
"# # # right=[(bbands.upperband, \"upperband\",),\n",
"# # # (bbands.lowerband, \"lowerband\",),\n",
"# # # (bbands.middleband, \"middleband\",)\n",
"# # # ], #[(series, name, entries, exits, other_markers)]\n",
"# middle1=[],\n",
"# middle2=[],\n",
"# title = \"1m\")\n",
"\n",
"ch = chart([pane1], sync=True, size=\"s\", xloc=slice(\"2024-02-20 09:30:00\",\"2024-02-22 16:00:00\"), precision=6)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# data = s5data.xloc[\"2024-01-03 09:30:00\":\"2024-03-10 16:00:00\"]\n",
"# entry = entry_ups.vbt.xloc[\"2024-01-03 09:30:00\":\"2024-03-10 16:00:00\"].obj\n",
"\n",
"pf = vbt.Portfolio.from_signals(close=s5data, entries=entry_ups, direction=\"longonly\", sl_stop=0.05/100, tp_stop = 0.05/100, fees=0.0167/100, freq=\"5s\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.xloc[\"2024-01-26 09:30:00\":\"2024-02-28 16:00:00\"].positions.plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.xloc[\"2024-01-26 09:30:00\":\"2024-01-28 16:00:00\"].plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pd.set_option('display.max_rows', None)\n",
"pf.stats()\n",
"# pf.xloc[\"monday\"].stats()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"buyvolume = t1data.data[\"BAC\"].buyvolume\n",
"sellvolume = t1data.data[\"BAC\"].sellvolume\n",
"totalvolume = buyvolume + sellvolume\n",
"\n",
"#adjust to minimal value to avoid division by zero\n",
"sellvolume_adjusted = sellvolume.replace(0, 1e-10)\n",
"oibratio = buyvolume / sellvolume\n",
"\n",
"#cumulative order flow (net difference)\n",
"cof = buyvolume - sellvolume\n",
"\n",
"# Calculate the order imbalance (net differene) normalize the order imbalance by calculating the difference between buy and sell volumes and then scaling it by the total volume.\n",
"order_imbalance = cof / totalvolume\n",
"order_imbalance = order_imbalance.fillna(0) #nan nahradime 0\n",
"\n",
"order_imbalance_allvolume = cof / t1data.data[\"BAC\"].volume\n",
"\n",
"order_imbalance_sma = vbt.indicator(\"talib:EMA\").run(order_imbalance, timeperiod=5)\n",
"short_signals = order_imbalance.vbt < -0.5\n",
"#short_entries = oibratio.vbt < 0.01\n",
"short_signals.value_counts()\n",
"short_signals.name = \"short_entries\"\n",
"#.fillna(False)\n",
"short_exits = short_signals.shift(-2).fillna(False).astype(bool)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pane1 = Panel(\n",
" ohlcv=(t1data.data[\"BAC\"],), #(series, entries, exits, other_markers)\n",
" histogram=[(order_imbalance_allvolume, \"oib_allvolume\", \"rgba(53, 94, 59, 0.6)\",0.5),\n",
" (t1data.data[\"BAC\"].trades, \"trades\",None,0.4),\n",
" ], # [(series, name, \"rgba(53, 94, 59, 0.6)\", opacity)]\n",
" # right=[\n",
" # (supertrend.trend,\"STtrend\"),\n",
" # (supertrend.long,\"STlong\"),\n",
" # (supertrend.short,\"STshort\")\n",
" # ],\n",
" # left = [(supertrend.direction,\"STdirection\")],\n",
" # right=[(bbands.upperband, \"upperband\",),\n",
" # (bbands.lowerband, \"lowerband\",),\n",
" # (bbands.middleband, \"middleband\",)\n",
" # ], #[(series, name, entries, exits, other_markers)]\n",
" middle1=[],\n",
" middle2=[],\n",
")\n",
"\n",
"pane2 = Panel(\n",
" ohlcv=(basic_data.data[\"BAC\"],), #(series, entries, exits, other_markers)\n",
" left=[(basic_data.data[\"BAC\"].trades, \"trades\")],\n",
" histogram=[(basic_data.data[\"BAC\"].trades, \"trades_hist\", \"white\", 0.5)], #\"rgba(53, 94, 59, 0.6)\"\n",
" # ], # [(series, name, \"rgba(53, 94, 59, 0.6)\")]\n",
" # right=[\n",
" # (supertrend.trend,\"STtrend\"),\n",
" # (supertrend.long,\"STlong\"),\n",
" # (supertrend.short,\"STshort\")\n",
" # ],\n",
" # left = [(supertrend.direction,\"STdirection\")],\n",
" # right=[(bbands.upperband, \"upperband\",),\n",
" # (bbands.lowerband, \"lowerband\",),\n",
" # (bbands.middleband, \"middleband\",)\n",
" # ], #[(series, name, entries, exits, other_markers)]\n",
" middle1=[],\n",
" middle2=[],\n",
")\n",
"\n",
"\n",
"ch = chart([pane1, pane2], size=\"m\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"#short_signal = t1slope.real_below(t1_th) & t2slope.real_below(t2_th) & t3slope.real_below(t3_th) & t4slope.real_below(t4_th)\n",
"#long_signal = t1slope.real_above(t1_th) & t2slope.real_above(t2_th) & t3slope.real_above(t3_th) & t4slope.real_above(t4_th)\n",
"\n",
"#test na daily s reversem crossed 0\n",
"short_signal = t2slope.vbt < -0.01 & t3slope.vbt < -0.01 #min value of threshold\n",
"long_signal = t2slope.vbt > 0.01 & t3slope.vbt > 0.01 #min\n",
"\n",
"# thirty_up_signal = t3slope.vbt.crossed_above(0.01)\n",
"# thirty_down_signal = t3slope.vbt.crossed_below(-0.01)\n",
"\n",
"fig = plot_2y_close(priminds=[], secinds=[t3slope], close=t1data.close)\n",
"#short_signal.vbt.signals.plot_as_entries(basic_data.close, fig=fig)\n",
"\n",
"short_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"SHORTS\",\n",
" line=dict(color=\"#ffe476\"),\n",
" marker=dict(color=\"red\", symbol=\"triangle-down\"),\n",
" fill=None,\n",
" connectgaps=True,\n",
" ))\n",
"long_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"LONGS\",\n",
" line=dict(color=\"#ffe476\"),\n",
" marker=dict(color=\"limegreen\"),\n",
" fill=None,\n",
" connectgaps=True,\n",
" ))\n",
"\n",
"# thirty_down_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"DOWN30\",\n",
"# line=dict(color=\"#ffe476\"),\n",
"# marker=dict(color=\"yellow\", symbol=\"triangle-down\"),\n",
"# fill=None,\n",
"# connectgaps=True,\n",
"# ))\n",
"# thirty_up_signal.vbt.signals.plot_as_entries(t1data.close, fig=fig, trace_kwargs=dict(name=\"UP30\",\n",
"# line=dict(color=\"#ffe476\"),\n",
"# marker=dict(color=\"grey\"),\n",
"# fill=None,\n",
"# connectgaps=True,\n",
"# ))\n",
"\n",
"# thirtymin_slope_to_compare.vbt.plot(fig=fig, add_trace_kwargs=dict(secondary_y=True), trace_kwargs=dict(name=\"30min slope\",\n",
"# line=dict(color=\"yellow\"), \n",
"# fill=None,\n",
"# connectgaps=True,\n",
"# ))\n",
"\n",
"fig.show()\n",
"# print(\"short signal\")\n",
"# print(short_signal.value_counts())\n",
"\n",
"#forced_exit = pd.Series(False, index=close.index)\n",
"forced_exit = basic_data.symbol_wrapper.fill(False)\n",
"#entry_window_open = pd.Series(False, index=close.index)\n",
"entry_window_open= basic_data.symbol_wrapper.fill(False)\n",
"\n",
"# Calculate the time difference in minutes from market open for each timestamp\n",
"elapsed_min_from_open = (forced_exit.index.hour - market_open.hour) * 60 + (forced_exit.index.minute - market_open.minute)\n",
"\n",
"entry_window_open[(elapsed_min_from_open >= entry_window_opens) & (elapsed_min_from_open < entry_window_closes)] = True\n",
"\n",
"#print(entry_window_open.value_counts())\n",
"\n",
"forced_exit[(elapsed_min_from_open >= forced_exit_start) & (elapsed_min_from_open < forced_exit_end)] = True\n",
"short_entries = (short_signal & entry_window_open)\n",
"short_exits = forced_exit\n",
"\n",
"entries = (long_signal & entry_window_open)\n",
"exits = forced_exit\n",
"#long_entries.info()\n",
"#number of trues and falses in long_entries\n",
"# print(short_exits.value_counts())\n",
"# print(short_entries.value_counts())\n",
"\n",
"#fig = plot_2y_close([],[momshort, rocp], close)\n",
"#short_signal.vbt.signals.plot_as_entries(close, fig=fig, add_trace_kwargs=dict(secondary_y=False))\n",
"#print(sl_stop)\n",
"#short_entries=short_entries, short_exits=short_exits,\n",
"# pf = vbt.Portfolio.from_signals(close=basic_data, entries=short_entries, exits=exits, tsl_stop=0.005, tp_stop = 0.05, fees=0.0167/100, freq=\"1s\") #sl_stop=sl_stop, tp_stop = sl_stop,\n",
"\n",
"# pf.stats()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"forced_exit = t1data.symbol_wrapper.fill(False)\n",
"#entry_window_open = pd.Series(False, index=close.index)\n",
"entry_window_open= t1data.symbol_wrapper.fill(False)\n",
"\n",
"# Calculate the time difference in minutes from market open for each timestamp\n",
"elapsed_min_from_open = (forced_exit.index.hour - market_open.hour) * 60 + (forced_exit.index.minute - market_open.minute)\n",
"\n",
"entry_window_open[(elapsed_min_from_open >= entry_window_opens) & (elapsed_min_from_open < entry_window_closes)] = True\n",
"\n",
"#print(entry_window_open.value_counts())\n",
"\n",
"forced_exit[(elapsed_min_from_open >= forced_exit_start) & (elapsed_min_from_open < forced_exit_end)] = True\n",
"short_entries = (short_signals & entry_window_open)\n",
"short_exits = forced_exit\n",
"\n",
"entries = (long_signals & entry_window_open)\n",
"exits = forced_exit\n",
"\n",
"pf = vbt.Portfolio.from_signals(close=t1data, entries=entries, exits=exits, short_entries=short_entries, short_exits=exits,\n",
"td_stop=2, time_delta_format=\"rows\",\n",
"tsl_stop=0.005, tp_stop = 0.005, fees=0.0167/100)#, freq=\"1s\") #sl_stop=sl_stop, tp_stop = sl_stop,\n",
"\n",
"pf.stats()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.plot()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.get_drawdowns().records_readable"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"pf.orders.records_readable"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.11"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

View File

@ -1,9 +1,9 @@
import numpy as np
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
from typing import Tuple
from copy import deepcopy
from v2realbot.strategy.base import StrategyState
from v2realbot.strategyblocks.activetrade.helpers import get_max_profit_price, get_profit_target_price, get_override_for_active_trade, keyword_conditions_met
from v2realbot.strategyblocks.activetrade.helpers import get_max_profit_price, get_profit_target_price, get_signal_section_directive, keyword_conditions_met
from v2realbot.utils.utils import safe_get
# FIBONACCI PRO PROFIT A SL
@ -63,10 +63,10 @@ class SLOptimizer:
def initialize_levels(self, state):
directive_name = 'SL_opt_exit_levels_'+str(self.direction)
SL_opt_exit_levels = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
SL_opt_exit_levels = get_signal_section_directive(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
directive_name = 'SL_opt_exit_sizes_'+str(self.direction)
SL_opt_exit_sizes = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
SL_opt_exit_sizes = get_signal_section_directive(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
if SL_opt_exit_levels is None or SL_opt_exit_sizes is not None:
print("no directives found: SL_opt_exit_levels/SL_opt_exit_sizes")

2
testy/testtt.py Normal file
View File

@ -0,0 +1,2 @@
import locale
print(locale.getdefaultlocale())

View File

@ -59,25 +59,12 @@ Hlavní loop:
"""
def next(data, state: StrategyState):
##print(10*"*","NEXT START",10*"*")
# important vars state.avgp, state.positions, state.vars, data
#indicators moved to call_next in upper class
print(10*"*", state.account_variables)
#pokud mame prazdne pozice a neceka se na nic
if state.positions == 0 and state.vars.pending is None:
#vykoname trady ve fronte
execute_prescribed_trades(state, data)
#pokud se neaktivoval nejaky trade, poustime signal search - ale jen jednou za bar?
#if conf_bar == 1:
if state.vars.pending is None:
signal_search(state, data)
#pro jistotu ihned zpracujeme
execute_prescribed_trades(state, data)
#mame aktivni trade a neceka se n anic
elif state.vars.activeTrade and state.vars.pending is None:
manage_active_trade(state, data)
execute_prescribed_trades(state, data)
signal_search(state, data)
execute_prescribed_trades(state, data) #pro jistotu ihned zpracujeme
manage_active_trade(state, data)
def init(state: StrategyState):
#place to declare new vars
@ -88,13 +75,13 @@ def init(state: StrategyState):
#nove atributy na rizeni tradu
#identifikuje provedenou změnu na Tradu (neděláme změny dokud nepřijde potvrzeni z notifikace)
state.vars.pending = None
#state.vars.pending = None #nahrazeno pebnding pod accountem state.account_variables[account.name].pending
#obsahuje aktivni Trade a jeho nastaveni
state.vars.activeTrade = None #pending/Trade
#state.vars.activeTrade = None #pending/Trade moved to account_variables
#obsahuje pripravene Trady ve frontě
state.vars.prescribedTrades = []
#flag pro reversal
state.vars.requested_followup = None
#state.vars.requested_followup = None #nahrazeno pod accountem
#TODO presunout inicializaci work_dict u podminek - sice hodnoty nepujdou zmenit, ale zlepsi se performance
#pripadne udelat refresh kazdych x-iterací
@ -102,9 +89,8 @@ def init(state: StrategyState):
state.vars.mode = None
state.vars.last_50_deltas = []
state.vars.next_new = 0
state.vars.last_buy_index = None
state.vars.last_exit_index = None
state.vars.last_in_index = None
state.vars.last_entry_index = None #mponechano obecne pro vsechny accounty
state.vars.last_exit_index = None #obecna varianta ponechana
state.vars.last_update_time = 0
state.vars.reverse_position_waiting_amount = 0
#INIT promenne, ktere byly zbytecne ve stratvars

View File

@ -39,7 +39,7 @@
"""
from uuid import UUID, uuid4
from alpaca.trading.enums import OrderSide, OrderStatus, TradeEvent, OrderType
from v2realbot.common.model import TradeUpdate, Order
from v2realbot.common.model import TradeUpdate, Order, Account
from rich import print as printanyway
import threading
import asyncio
@ -61,6 +61,7 @@ import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output
from dash import dcc, html, dash_table, Dash
import v2realbot.utils.config_handler as cfh
from typing import Set
""""
LATENCY DELAYS
.000 trigger - last_trade_time (.4246266)
@ -74,7 +75,20 @@ lock = threading.Lock
#todo nejspis dat do classes, aby se mohlo backtestovat paralelne
#ted je globalni promena last_time_now a self.account a cash
class Backtester:
def __init__(self, symbol: str, order_fill_callback: callable, btdata: list, bp_from: datetime, bp_to: datetime, cash: float = 100000):
"""
Initializes a new instance of the Backtester class.
Args:
symbol (str): The symbol of the security being backtested.
accounts (set): A set of accounts to use for backtesting.
order_fill_callback (callable): A callback function to handle order fills.
btdata (list): A list of backtesting data.
bp_from (datetime): The start date of the backtesting period.
bp_to (datetime): The end date of the backtesting period.
cash (float, optional): The initial cash balance. Defaults to 100000.
Returns:
None
"""
def __init__(self, symbol: str, accounts: Set, order_fill_callback: callable, btdata: list, bp_from: datetime, bp_to: datetime, cash: float = 100000):
#this TIME value determines true time for submit, replace, cancel order should happen (allowing past)
#it is set by every iteration of BT or before fill callback - allowing past events to happen
self.time = None
@ -83,6 +97,7 @@ class Backtester:
self.btdata = btdata
self.backtest_start = None
self.backtest_end = None
self.accounts = accounts
self.cash_init = cash
#backtesting period
self.bp_from = bp_from
@ -90,9 +105,10 @@ class Backtester:
self.cash = cash
self.cash_reserved_for_shorting = 0
self.trades = []
self.account = { "BAC": [0, 0] }
# { "BAC": [avgp, size] }
self.open_orders =[]
self.internal_account = { account.name:{self.symbol: [0, 0]} for account in accounts }
# { "ACCOUNT1": {}"BAC": [avgp, size]}, .... }
self.open_orders =[] #open orders shared for all accounts, account being an attribute
# self.open_orders = [Order(id=uuid4(),
# submitted_at = datetime(2023, 3, 17, 9, 30, 0, 0, tzinfo=zoneNY),
# symbol = "BAC",
@ -110,6 +126,8 @@ class Backtester:
# side = OrderSide.BUY)]
#
def execute_orders_and_callbacks(self, intime: float):
"""""
Voláno ze strategie před každou iterací s časem T.
@ -166,7 +184,7 @@ class Backtester:
for order in self.open_orders:
#pokud je vyplneny symbol, tak jedeme jen tyto, jinak vsechny
print(order.id, datetime.timestamp(order.submitted_at), order.symbol, order.side, order.order_type, order.qty, order.limit_price, order.submitted_at)
print(order.account.name, order.id, datetime.timestamp(order.submitted_at), order.symbol, order.side, order.order_type, order.qty, order.limit_price, order.submitted_at)
if order.canceled_at:
#ic("deleting canceled order",order.id)
todel.append(order)
@ -348,21 +366,22 @@ class Backtester:
#ic(o.filled_at, o.filled_avg_price)
a = self.update_account(o = o)
a = self.update_internal_account(o = o)
if a < 0:
tlog("update_account ERROR")
return -1
trade = TradeUpdate(order = o,
trade = TradeUpdate(account=o.account,
order = o,
event = TradeEvent.FILL,
execution_id = str(uuid4()),
timestamp = datetime.fromtimestamp(fill_time),
position_qty= self.account[o.symbol][0],
position_qty= self.internal_account[o.account.name][o.symbol][0],
price=float(fill_price),
qty = o.qty,
value = float(o.qty*fill_price),
cash = self.cash,
pos_avg_price = self.account[o.symbol][1])
pos_avg_price = self.internal_account[o.account.name][o.symbol][1])
self.trades.append(trade)
@ -379,49 +398,49 @@ class Backtester:
self.time = time + float(cfh.config_handler.get_val('BT_DELAYS','fill_to_not'))
print("current bt.time",self.time)
#print("FILL NOTIFICATION: ", tradeupdate)
res = asyncio.run(self.order_fill_callback(tradeupdate))
res = asyncio.run(self.order_fill_callback(tradeupdate, tradeupdate.account))
return 0
def update_account(self, o: Order):
def update_internal_account(self, o: Order):
#updatujeme self.account
#pokud neexistuje klic v accountu vytvorime si ho
if o.symbol not in self.account:
if o.symbol not in self.internal_account[o.account.name]:
# { "BAC": [size, avgp] }
self.account[o.symbol] = [0,0]
self.internal_account[o.account.name][o.symbol] = [0,0]
if o.side == OrderSide.BUY:
#[size, avgp]
newsize = (self.account[o.symbol][0] + o.qty)
newsize = (self.internal_account[o.account.name][o.symbol][0] + o.qty)
#JPLNE UZAVRENI SHORT (avgp 0)
if newsize == 0: newavgp = 0
#CASTECNE UZAVRENI SHORT (avgp puvodni)
elif newsize < 0: newavgp = self.account[o.symbol][1]
elif newsize < 0: newavgp = self.internal_account[o.account.name][o.symbol][1]
#JDE O LONG (avgp nove)
else:
newavgp = ((self.account[o.symbol][0] * self.account[o.symbol][1]) + (o.qty * o.filled_avg_price)) / (self.account[o.symbol][0] + o.qty)
newavgp = ((self.internal_account[o.account.name][o.symbol][0] * self.internal_account[o.account.name][o.symbol][1]) + (o.qty * o.filled_avg_price)) / (self.internal_account[o.account.name][o.symbol][0] + o.qty)
self.account[o.symbol] = [newsize, newavgp]
self.internal_account[o.account.name][o.symbol] = [newsize, newavgp]
self.cash = self.cash - (o.qty * o.filled_avg_price)
return 1
#sell
elif o.side == OrderSide.SELL:
newsize = self.account[o.symbol][0]-o.qty
newsize = self.internal_account[o.account.name][o.symbol][0]-o.qty
#UPLNE UZAVRENI LONGU (avgp 0)
if newsize == 0: newavgp = 0
#CASTECNE UZAVRENI LONGU (avgp puvodni)
elif newsize > 0: newavgp = self.account[o.symbol][1]
elif newsize > 0: newavgp = self.internal_account[o.account.name][o.symbol][1]
#jde o SHORT (avgp nove)
else:
#pokud je predchozi 0 - tzn. jde o prvni short
if self.account[o.symbol][1] == 0:
if self.internal_account[o.account.name][o.symbol][1] == 0:
newavgp = o.filled_avg_price
else:
newavgp = ((abs(self.account[o.symbol][0]) * self.account[o.symbol][1]) + (o.qty * o.filled_avg_price)) / (abs(self.account[o.symbol][0]) + o.qty)
newavgp = ((abs(self.internal_account[o.account.name][o.symbol][0]) * self.internal_account[o.account.name][o.symbol][1]) + (o.qty * o.filled_avg_price)) / (abs(self.internal_account[o.account.name][o.symbol][0]) + o.qty)
self.account[o.symbol] = [newsize, newavgp]
self.internal_account[o.account.name][o.symbol] = [newsize, newavgp]
#pokud jde o prodej longu(nova pozice je>=0) upravujeme cash
if self.account[o.symbol][0] >= 0:
if self.internal_account[o.account.name][o.symbol][0] >= 0:
self.cash = float(self.cash + (o.qty * o.filled_avg_price))
print("uprava cashe, jde o prodej longu")
else:
@ -466,7 +485,7 @@ class Backtester:
# #ic("get last price")
# return self.btdata[i-1][1]
def submit_order(self, time: float, symbol: str, side: OrderSide, size: int, order_type: OrderType, price: float = None):
def submit_order(self, time: float, symbol: str, side: OrderSide, size: int, order_type: OrderType, account: Account, price: float = None):
"""submit order
- zakladni validace
- vloží do self.open_orders s daným časem
@ -499,9 +518,9 @@ class Backtester:
return -1
#pokud neexistuje klic v accountu vytvorime si ho
if symbol not in self.account:
if symbol not in self.internal_account[account.name]:
# { "BAC": [size, avgp] }
self.account[symbol] = [0,0]
self.internal_account[account.name][symbol] = [0,0]
#check for available quantity
if side == OrderSide.SELL:
@ -509,15 +528,15 @@ class Backtester:
reserved_price = 0
#with lock:
for o in self.open_orders:
if o.side == OrderSide.SELL and o.symbol == symbol and o.canceled_at is None:
if o.side == OrderSide.SELL and o.symbol == symbol and o.canceled_at is None and o.account==account:
reserved += o.qty
cena = o.limit_price if o.limit_price else self.get_last_price(time, o.symbol)
reserved_price += o.qty * cena
print("blokovano v open orders pro sell qty: ", reserved, "celkem:", reserved_price)
actual_minus_reserved = int(self.account[symbol][0]) - reserved
actual_minus_reserved = int(self.internal_account[account.name][symbol][0]) - reserved
if actual_minus_reserved > 0 and actual_minus_reserved - int(size) < 0:
printanyway("not enough shares available to sell or shorting while long position",self.account[symbol][0],"reserved",reserved,"available",int(self.account[symbol][0]) - reserved,"selling",size)
printanyway("not enough shares available to sell or shorting while long position",self.internal_account[account.name][symbol][0],"reserved",reserved,"available",int(self.internal_account[account.name][symbol][0]) - reserved,"selling",size)
return -1
#if is shorting - check available cash to short
@ -533,13 +552,13 @@ class Backtester:
reserved_price = 0
#with lock:
for o in self.open_orders:
if o.side == OrderSide.BUY and o.canceled_at is None:
if o.side == OrderSide.BUY and o.canceled_at is None and o.account==account:
cena = o.limit_price if o.limit_price else self.get_last_price(time, o.symbol)
reserved_price += o.qty * cena
reserved_qty += o.qty
print("blokovano v open orders for buy: qty, price", reserved_qty, reserved_price)
actual_plus_reserved_qty = int(self.account[symbol][0]) + reserved_qty
actual_plus_reserved_qty = int(self.internal_account[account.name][symbol][0]) + reserved_qty
#jde o uzavreni shortu
if actual_plus_reserved_qty < 0 and (actual_plus_reserved_qty + int(size)) > 0:
@ -555,6 +574,7 @@ class Backtester:
id = str(uuid4())
order = Order(id=id,
account=account,
submitted_at = datetime.fromtimestamp(float(time)),
symbol=symbol,
order_type = order_type,
@ -569,7 +589,7 @@ class Backtester:
return id
def replace_order(self, id: str, time: float, size: int = None, price: float = None):
def replace_order(self, id: str, time: float, account: Account, size: int = None, price: float = None):
"""replace order
- zakladni validace vrací synchronně
- vrací číslo nové objednávky
@ -586,7 +606,7 @@ class Backtester:
#with lock:
for o in self.open_orders:
print(o.id)
if str(o.id) == str(id):
if str(o.id) == str(id) and o.account == account:
newid = str(uuid4())
o.id = newid
o.submitted_at = datetime.fromtimestamp(time)
@ -597,7 +617,7 @@ class Backtester:
print("BT: replacement order doesnt exist")
return 0
def cancel_order(self, time: float, id: str):
def cancel_order(self, time: float, id: str, account: Account):
"""cancel order
- základní validace vrací synchronně
- vymaže objednávku z open orders
@ -613,22 +633,22 @@ class Backtester:
return 0
#with lock:
for o in self.open_orders:
if str(o.id) == id:
if str(o.id) == id and o.account == account:
o.canceled_at = time
print("set as canceled in self.open_orders")
return 1
print("BTC: cantchange. open order doesnt exist")
return 0
def get_open_position(self, symbol: str):
def get_open_position(self, symbol: str, account: Account):
"""get positions ->(avg,size)"""
#print("BT:get open positions entry")
try:
return self.account[symbol][1], self.account[symbol][0]
return self.internal_account[account.name][symbol][1], self.internal_account[account.name][symbol][0]
except:
return (0,0)
def get_open_orders(self, side: OrderSide, symbol: str):
def get_open_orders(self, side: OrderSide, symbol: str, account: Account):
"""get open orders ->list(OrderNotification)"""
print("BT:get open orders entry")
if len(self.open_orders) == 0:
@ -638,7 +658,7 @@ class Backtester:
#with lock:
for o in self.open_orders:
#print(o)
if o.symbol == symbol and o.canceled_at is None:
if o.symbol == symbol and o.canceled_at is None and o.account == account:
if side is None or o.side == side:
res.append(o)
return res

View File

@ -1,41 +0,0 @@
from enum import Enum
from datetime import datetime
from pydantic import BaseModel
from typing import Any, Optional, List, Union
from uuid import UUID
class TradeStatus(str, Enum):
READY = "ready"
ACTIVATED = "activated"
CLOSED = "closed"
#FINISHED = "finished"
class TradeDirection(str, Enum):
LONG = "long"
SHORT = "short"
class TradeStoplossType(str, Enum):
FIXED = "fixed"
TRAILING = "trailing"
#Predpis obchodu vygenerovany signalem, je to zastresujici jednotka
#ke kteremu jsou pak navazany jednotlivy FILLy (reprezentovany model.TradeUpdate) - napr. castecne exity atp.
class Trade(BaseModel):
id: UUID
last_update: datetime
entry_time: Optional[datetime] = None
exit_time: Optional[datetime] = None
status: TradeStatus
generated_by: Optional[str] = None
direction: TradeDirection
entry_price: Optional[float] = None
goal_price: Optional[float] = None
size: Optional[int] = None
# size_multiplier je pomocna promenna pro pocitani relativniho denniho profit
size_multiplier: Optional[float] = None
# stoploss_type: TradeStoplossType
stoploss_value: Optional[float] = None
profit: Optional[float] = 0
profit_sum: Optional[float] = 0
rel_profit: Optional[float] = 0
rel_profit_cum: Optional[float] = 0

View File

@ -5,10 +5,75 @@ from rich import print
from typing import Any, Optional, List, Union
from datetime import datetime, date
from pydantic import BaseModel, Field
from v2realbot.enums.enums import Mode, Account, SchedulerStatus, Moddus, Market
from v2realbot.enums.enums import Mode, Account, SchedulerStatus, Moddus, Market, Followup
from alpaca.data.enums import Exchange
from enum import Enum
from datetime import datetime
from pydantic import BaseModel
from typing import Any, Optional, List, Union
from uuid import UUID
#prescribed model
#from prescribed model
class InstantIndicator(BaseModel):
name: str
toml: str
class TradeStatus(str, Enum):
READY = "ready"
ACTIVATED = "activated"
CLOSED = "closed"
#FINISHED = "finished"
class TradeDirection(str, Enum):
LONG = "long"
SHORT = "short"
class TradeStoplossType(str, Enum):
FIXED = "fixed"
TRAILING = "trailing"
#Predpis obchodu vygenerovany signalem, je to zastresujici jednotka
#ke kteremu jsou pak navazany jednotlivy FILLy (reprezentovany model.TradeUpdate) - napr. castecne exity atp.
class Trade(BaseModel):
account: Account
id: UUID
last_update: datetime
entry_time: Optional[datetime] = None
exit_time: Optional[datetime] = None
status: TradeStatus
generated_by: Optional[str] = None
direction: TradeDirection
entry_price: Optional[float] = None
goal_price: Optional[float] = None
size: Optional[int] = None
# size_multiplier je pomocna promenna pro pocitani relativniho denniho profit
size_multiplier: Optional[float] = None
# stoploss_type: TradeStoplossType
stoploss_value: Optional[float] = None
profit: Optional[float] = 0
profit_sum: Optional[float] = 0
rel_profit: Optional[float] = 0
rel_profit_cum: Optional[float] = 0
#account variables that can be accessed by ACCOUNT key dictionary
class AccountVariables(BaseModel):
positions: float = 0
avgp: float = 0
pending: str = None
blockbuy: int = 0
wait_for_fill: float = None
profit: float = 0
docasny_rel_profit: list = []
rel_profit_cum: list = []
last_entry_index: int = None #acc varianta, mame taky obnecnou state.vars.last_entry_index
requested_followup: Followup = None
activeTrade: Trade = None
dont_exit_already_activated: bool = False
#activeTrade, prescribedTrades
#tbd transferables?
#models for server side datatables
@ -91,7 +156,7 @@ class TestList(BaseModel):
dates: List[Intervals]
#for GUI to fetch historical trades on given symbol
class Trade(BaseModel):
class TradeView(BaseModel):
symbol: str
timestamp: datetime
exchange: Optional[Union[Exchange, str]] = None
@ -189,8 +254,8 @@ class RunnerView(BaseModel):
run_symbol: Optional[str] = None
run_trade_count: Optional[int] = 0
run_profit: Optional[float] = 0
run_positions: Optional[int] = 0
run_avgp: Optional[float] = 0
run_positions: Optional[dict] = 0
run_avgp: Optional[dict] = 0
run_stopped: Optional[datetime] = None
run_paused: Optional[datetime] = None
@ -208,8 +273,8 @@ class Runner(BaseModel):
run_ilog_save: Optional[bool] = False
run_trade_count: Optional[int] = None
run_profit: Optional[float] = None
run_positions: Optional[int] = None
run_avgp: Optional[float] = None
run_positions: Optional[dict] = None
run_avgp: Optional[dict] = None
run_strat_json: Optional[str] = None
run_stopped: Optional[datetime] = None
run_paused: Optional[datetime] = None
@ -247,6 +312,7 @@ class Bar(BaseModel):
vwap: Optional[float] = 0
class Order(BaseModel):
account: Account
id: UUID
submitted_at: datetime
filled_at: Optional[datetime] = None
@ -262,6 +328,7 @@ class Order(BaseModel):
#entita pro kazdy kompletni FILL, je navazana na prescribed_trade
class TradeUpdate(BaseModel):
account: Account
event: Union[TradeEvent, str]
execution_id: Optional[UUID] = None
order: Order
@ -307,8 +374,8 @@ class RunArchive(BaseModel):
ilog_save: Optional[bool] = False
profit: float = 0
trade_count: int = 0
end_positions: int = 0
end_positions_avgp: float = 0
end_positions: Union[dict,str] = None
end_positions_avgp: Union[dict,str] = None
metrics: Union[dict, str] = None
stratvars_toml: Optional[str] = None
@ -329,8 +396,8 @@ class RunArchiveView(BaseModel):
ilog_save: Optional[bool] = False
profit: float = 0
trade_count: int = 0
end_positions: int = 0
end_positions_avgp: float = 0
end_positions: Union[dict,int] = None
end_positions_avgp: Union[dict,float] = None
metrics: Union[dict, str] = None
batch_profit: float = 0 # Total profit for the batch - now calculated during query
batch_count: int = 0 # Count of runs in the batch - now calculated during query
@ -347,6 +414,8 @@ class SLHistory(BaseModel):
id: Optional[UUID] = None
time: datetime
sl_val: float
direction: TradeDirection
account: Account
#Contains archive of running strategies (runner) - detail data
class RunArchiveDetail(BaseModel):
@ -359,9 +428,3 @@ class RunArchiveDetail(BaseModel):
trades: List[TradeUpdate]
ext_data: Optional[dict] = None
class InstantIndicator(BaseModel):
name: str
toml: str

View File

@ -51,8 +51,8 @@ def row_to_runarchiveview(row: dict) -> RunArchiveView:
ilog_save=bool(row['ilog_save']),
profit=float(row['profit']),
trade_count=int(row['trade_count']),
end_positions=int(row['end_positions']),
end_positions_avgp=float(row['end_positions_avgp']),
end_positions=orjson.loads(row['end_positions']),
end_positions_avgp=orjson.loads(row['end_positions_avgp']),
metrics=orjson.loads(row['metrics']) if row['metrics'] else None,
batch_profit=int(row['batch_profit']) if row['batch_profit'] and row['batch_id'] else 0,
batch_count=int(row['batch_count']) if row['batch_count'] and row['batch_id'] else 0,
@ -79,8 +79,8 @@ def row_to_runarchive(row: dict) -> RunArchive:
ilog_save=bool(row['ilog_save']),
profit=float(row['profit']),
trade_count=int(row['trade_count']),
end_positions=int(row['end_positions']),
end_positions_avgp=float(row['end_positions_avgp']),
end_positions=str(row['end_positions']),
end_positions_avgp=str(row['end_positions_avgp']),
metrics=orjson.loads(row['metrics']),
stratvars_toml=row['stratvars_toml'],
transferables=orjson.loads(row['transferables']) if row['transferables'] else None

View File

@ -17,9 +17,6 @@ RUNNER_DETAIL_DIRECTORY = Path(__file__).parent.parent.parent / "runner_detail"
LOG_PATH = Path(__file__).parent.parent
LOG_FILE = Path(__file__).parent.parent / "strat.log"
JOB_LOG_FILE = Path(__file__).parent.parent / "job.log"
DOTENV_DIRECTORY = Path(__file__).parent.parent.parent
ENV_FILE = DOTENV_DIRECTORY / '.env'
#stratvars that cannot be changed in gui
STRATVARS_UNCHANGEABLES = ['pendingbuys', 'blockbuy', 'jevylozeno', 'limitka']
@ -30,6 +27,26 @@ MODEL_DIR = Path(DATA_DIR)/"models"
PROFILING_NEXT_ENABLED = False
PROFILING_OUTPUT_DIR = DATA_DIR
def find_dotenv(start_path):
"""
Searches for a .env file in the given directory or its parents and returns the path.
Args:
start_path: The directory to start searching from.
Returns:
Path to the .env file if found, otherwise None.
"""
current_path = Path(start_path)
for _ in range(6): # Limit search depth to 5 levels
dotenv_path = current_path / '.env'
if dotenv_path.exists():
return dotenv_path
current_path = current_path.parent
return None
ENV_FILE = find_dotenv(__file__)
#NALOADUJEME DOTENV ENV VARIABLES
if load_dotenv(ENV_FILE, verbose=True) is False:
print(f"Error loading.env file {ENV_FILE}. Now depending on ENV VARIABLES set externally.")
@ -66,10 +83,10 @@ def get_key(mode: Mode, account: Account):
return None
dict = globals()
try:
API_KEY = dict[str.upper(str(account.value)) + "_" + str.upper(str(mode.value)) + "_API_KEY" ]
SECRET_KEY = dict[str.upper(str(account.value)) + "_" + str.upper(str(mode.value)) + "_SECRET_KEY" ]
PAPER = dict[str.upper(str(account.value)) + "_" + str.upper(str(mode.value)) + "_PAPER" ]
FEED = dict[str.upper(str(account.value)) + "_" + str.upper(str(mode.value)) + "_FEED" ]
API_KEY = dict[str.upper(str(account.name)) + "_" + str.upper(str(mode.name)) + "_API_KEY" ]
SECRET_KEY = dict[str.upper(str(account.name)) + "_" + str.upper(str(mode.name)) + "_SECRET_KEY" ]
PAPER = dict[str.upper(str(account.name)) + "_" + str.upper(str(mode.name)) + "_PAPER" ]
FEED = dict[str.upper(str(account.name)) + "_" + str.upper(str(mode.name)) + "_FEED" ]
return Keys(API_KEY, SECRET_KEY, PAPER, FEED)
except KeyError:
print("Not valid combination to get keys for", mode, account)
@ -93,7 +110,7 @@ data_feed_type_str = os.environ.get('ACCOUNT1_PAPER_FEED', 'iex') # Default to
# Convert the string to DataFeed enum
try:
ACCOUNT1_PAPER_FEED = DataFeed(data_feed_type_str)
except ValueError:
except nameError:
# Handle the case where the environment variable does not match any enum member
print(f"Invalid data feed type: {data_feed_type_str} in ACCOUNT1_PAPER_FEED defaulting to 'iex'")
ACCOUNT1_PAPER_FEED = DataFeed.SIP
@ -111,7 +128,7 @@ data_feed_type_str = os.environ.get('ACCOUNT1_LIVE_FEED', 'iex') # Default to '
# Convert the string to DataFeed enum
try:
ACCOUNT1_LIVE_FEED = DataFeed(data_feed_type_str)
except ValueError:
except nameError:
# Handle the case where the environment variable does not match any enum member
print(f"Invalid data feed type: {data_feed_type_str} in ACCOUNT1_LIVE_FEED defaulting to 'iex'")
ACCOUNT1_LIVE_FEED = DataFeed.IEX
@ -129,7 +146,7 @@ data_feed_type_str = os.environ.get('ACCOUNT2_PAPER_FEED', 'iex') # Default to
# Convert the string to DataFeed enum
try:
ACCOUNT2_PAPER_FEED = DataFeed(data_feed_type_str)
except ValueError:
except nameError:
# Handle the case where the environment variable does not match any enum member
print(f"Invalid data feed type: {data_feed_type_str} in ACCOUNT2_PAPER_FEED defaulting to 'iex'")
ACCOUNT2_PAPER_FEED = DataFeed.IEX
@ -148,7 +165,7 @@ except ValueError:
# # Convert the string to DataFeed enum
# try:
# ACCOUNT2_LIVE_FEED = DataFeed(data_feed_type_str)
# except ValueError:
# except nameError:
# # Handle the case where the environment variable does not match any enum member
# print(f"Invalid data feed type: {data_feed_type_str} in ACCOUNT2_LIVE_FEED defaulting to 'iex'")
# ACCOUNT2_LIVE_FEED = DataFeed.IEX

View File

@ -3,7 +3,7 @@ from uuid import UUID, uuid4
from v2realbot.common.model import RunManagerRecord, StrategyInstance, RunDay, StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, RunArchiveChange, Bar, TradeEvent, TestList, Intervals, ConfigItem, InstantIndicator, DataTablesRequest
from v2realbot.utils.utils import validate_and_format_time, AttributeDict, zoneNY, zonePRG, safe_get, dict_replace_value, Store, parse_toml_string, json_serial, is_open_hours, send_to_telegram, concatenate_weekdays, transform_data
from v2realbot.utils.ilog import delete_logs
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
from v2realbot.common.model import Trade, TradeDirection, TradeStatus, TradeStoplossType
from datetime import datetime
from v2realbot.loader.trade_offline_streamer import Trade_Offline_Streamer
from threading import Thread, current_thread, Event, enumerate

View File

@ -8,9 +8,9 @@ from alpaca.data.timeframe import TimeFrame
from v2realbot.strategy.base import StrategyState
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, OrderSide
from v2realbot.common.model import RunDay, StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, RunArchiveChange, Bar, TradeEvent, TestList, Intervals, ConfigItem, InstantIndicator, DataTablesRequest
from v2realbot.utils.utils import AttributeDict, zoneNY, zonePRG, safe_get, dict_replace_value, Store, parse_toml_string, json_serial, is_open_hours, send_to_telegram, concatenate_weekdays, transform_data
from v2realbot.utils.utils import AttributeDict, zoneNY, zonePRG, safe_get, dict_replace_value, Store, parse_toml_string, json_serial, is_open_hours, send_to_telegram, concatenate_weekdays, transform_data, gaka
from v2realbot.utils.ilog import delete_logs
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
from v2realbot.common.model import Trade, TradeDirection, TradeStatus, TradeStoplossType
from datetime import datetime
from v2realbot.loader.trade_offline_streamer import Trade_Offline_Streamer
from threading import Thread, current_thread, Event, enumerate
@ -71,8 +71,8 @@ def get_all_runners():
if i.run_instance:
i.run_profit = round(float(i.run_instance.state.profit),2)
i.run_trade_count = len(i.run_instance.state.tradeList)
i.run_positions = i.run_instance.state.positions
i.run_avgp = round(float(i.run_instance.state.avgp),3)
i.run_positions = gaka(i.run_instance.state.account_variables, "positions")
i.run_avgp = gaka(i.run_instance.state.account_variables, "avgp", lambda x: round(float(x),3))
return (0, db.runners)
else:
return (0, [])
@ -94,8 +94,8 @@ def get_runner(id: UUID):
if str(i.id) == str(id):
i.run_profit = round(float(i.run_instance.state.profit),2)
i.run_trade_count = len(i.run_instance.state.tradeList)
i.run_positions = i.run_instance.state.positions
i.run_avgp = round(float(i.run_instance.state.avgp),3)
i.run_positions =gaka(i.run_instance.state.account_variables, "positions")
i.run_avgp = gaka(i.run_instance.state.account_variables, "avgp", lambda x: round(float(x),3))
return (0, i)
return (-2, "not found")
@ -738,13 +738,14 @@ def populate_metrics_output_directory(strat: StrategyInstance, inter_batch_param
tradeList = strat.state.tradeList
trade_dict = AttributeDict(orderid=[],timestamp=[],symbol=[],side=[],order_type=[],qty=[],price=[],position_qty=[])
trade_dict = AttributeDict(account=[],orderid=[],timestamp=[],symbol=[],side=[],order_type=[],qty=[],price=[],position_qty=[])
if strat.mode == Mode.BT:
trade_dict["value"] = []
trade_dict["cash"] = []
trade_dict["pos_avg_price"] = []
for t in tradeList:
if t.event == TradeEvent.FILL:
trade_dict.account.append(t.account)
trade_dict.orderid.append(str(t.order.id))
trade_dict.timestamp.append(t.timestamp)
trade_dict.symbol.append(t.order.symbol)
@ -768,10 +769,12 @@ def populate_metrics_output_directory(strat: StrategyInstance, inter_batch_param
max_positions = max_positions[max_positions['side'] == OrderSide.SELL]
max_positions = max_positions.drop(columns=['side'], axis=1)
res = dict(profit={})
res = dict(account_variables={}, profit={})
#filt = max_positions['side'] == 'OrderSide.BUY'
res["pos_cnt"] = dict(zip(str(max_positions['qty']), max_positions['count']))
res["account_variables"] = transform_data(strat.state.account_variables, json_serial)
res["pos_cnt"] = dict(zip(str(max_positions['qty']), max_positions['count']))
#naplneni batch sum profitu
if inter_batch_params is not None:
res["profit"]["batch_sum_profit"] = int(inter_batch_params["batch_profit"])
@ -923,8 +926,8 @@ def archive_runner(runner: Runner, strat: StrategyInstance, inter_batch_params:
settings = settings,
profit=round(float(strat.state.profit),2),
trade_count=len(strat.state.tradeList),
end_positions=strat.state.positions,
end_positions_avgp=round(float(strat.state.avgp),3),
end_positions=gaka(strat.state.account_variables, "positions"),
end_positions_avgp=gaka(strat.state.account_variables, "avgp", lambda x: round(float(x),3)),
metrics=results_metrics,
stratvars_toml=runner.run_stratvars_toml,
transferables=strat.state.vars["transferables"]
@ -1264,6 +1267,7 @@ def insert_archive_header(archeader: RunArchive):
try:
c = conn.cursor()
#json_string = orjson.dumps(archeader, default=json_serial, option=orjson.OPT_PASSTHROUGH_DATETIME)
#print(archeader)
res = c.execute("""
INSERT INTO runner_header
@ -1271,7 +1275,7 @@ def insert_archive_header(archeader: RunArchive):
VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(str(archeader.id), str(archeader.strat_id), archeader.batch_id, archeader.symbol, archeader.name, archeader.note, archeader.started, archeader.stopped, archeader.mode, archeader.account, archeader.bt_from, archeader.bt_to, orjson.dumps(archeader.strat_json).decode('utf-8'), orjson.dumps(archeader.settings).decode('utf-8'), archeader.ilog_save, archeader.profit, archeader.trade_count, archeader.end_positions, archeader.end_positions_avgp, orjson.dumps(archeader.metrics, default=json_serial, option=orjson.OPT_PASSTHROUGH_DATETIME).decode('utf-8'), archeader.stratvars_toml, orjson.dumps(archeader.transferables).decode('utf-8')))
(str(archeader.id), str(archeader.strat_id), archeader.batch_id, archeader.symbol, archeader.name, archeader.note, archeader.started, archeader.stopped, archeader.mode, archeader.account, archeader.bt_from, archeader.bt_to, orjson.dumps(archeader.strat_json).decode('utf-8'), orjson.dumps(archeader.settings).decode('utf-8'), archeader.ilog_save, archeader.profit, archeader.trade_count, orjson.dumps(archeader.end_positions).decode('utf-8'), orjson.dumps(archeader.end_positions_avgp).decode('utf-8'), orjson.dumps(archeader.metrics, default=json_serial, option=orjson.OPT_PASSTHROUGH_DATETIME).decode('utf-8'), archeader.stratvars_toml, orjson.dumps(archeader.transferables).decode('utf-8')))
#retry not yet supported for statement format above
#res = execute_with_retry(c,statement)
@ -1646,13 +1650,14 @@ def preview_indicator_byTOML(id: UUID, indicator: InstantIndicator, save: bool =
new_inds = AttributeDict(**new_inds)
new_tick_inds = {key: [] for key in detail.indicators[1].keys()}
new_tick_inds = AttributeDict(**new_tick_inds)
interface = BacktestInterface(symbol="X", bt=None)
def_account = Account("ACCOUNT1")
interface = BacktestInterface(symbol="X", bt=None, account=def_account)
##dame nastaveni indikatoru do tvaru, ktery stratvars ocekava (pro dynmaicke inicializace)
stratvars = AttributeDict(indicators=AttributeDict(**{jmeno:toml_parsed}))
#print("stratvars", stratvars)
state = StrategyState(name="XX", symbol = "X", stratvars = AttributeDict(**stratvars), interface=interface)
state = StrategyState(name="XX", symbol = "X", stratvars = AttributeDict(**stratvars), interface=interface, accounts=[def_account], account=def_account)
#inicializujeme stavove promenne a novy indikator v cilovem dict
if output == "bar":

View File

@ -5,6 +5,7 @@ from v2realbot.backtesting.backtester import Backtester
from datetime import datetime
from v2realbot.utils.utils import zoneNY
import v2realbot.utils.config_handler as cfh
from v2realbot.common.model import Account
""""
backtester methods can be called
@ -16,8 +17,9 @@ both should be backtestable
if method are called for the past self.time must be set accordingly
"""
class BacktestInterface(GeneralInterface):
def __init__(self, symbol, bt: Backtester) -> None:
def __init__(self, symbol, bt: Backtester, account: Account) -> None:
self.symbol = symbol
self.account = account
self.bt = bt
self.count_api_requests = cfh.config_handler.get_val('COUNT_API_REQUESTS')
self.mincnt = list([dict(minute=0,count=0)])
@ -43,48 +45,48 @@ class BacktestInterface(GeneralInterface):
def buy(self, size = 1, repeat: bool = False):
self.count()
#add REST API latency
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.BUY,size=size,order_type = OrderType.MARKET)
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.BUY,size=size,order_type = OrderType.MARKET, account=self.account)
"""buy limit"""
def buy_l(self, price: float, size: int = 1, repeat: bool = False, force: int = 0):
self.count()
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.BUY,size=size,price=price,order_type = OrderType.LIMIT)
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.BUY,size=size,price=price,order_type = OrderType.LIMIT, account=self.account)
"""sell market"""
def sell(self, size = 1, repeat: bool = False):
self.count()
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.SELL,size=size,order_type = OrderType.MARKET)
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.SELL,size=size,order_type = OrderType.MARKET, account=self.account)
"""sell limit"""
async def sell_l(self, price: float, size = 1, repeat: bool = False):
self.count()
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.SELL,size=size,price=price,order_type = OrderType.LIMIT)
return self.bt.submit_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),symbol=self.symbol,side=OrderSide.SELL,size=size,price=price,order_type = OrderType.LIMIT, account=self.account)
"""replace order"""
async def repl(self, orderid: str, price: float = None, size: int = None, repeat: bool = False):
self.count()
return self.bt.replace_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),id=orderid,size=size,price=price)
return self.bt.replace_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'),id=orderid,size=size,price=price, account=self.account)
"""cancel order"""
#TBD exec predtim?
def cancel(self, orderid: str):
self.count()
return self.bt.cancel_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'), id=orderid)
return self.bt.cancel_order(time=self.bt.time + cfh.config_handler.get_val('BT_DELAYS','strat_to_sub'), id=orderid, account=self.account)
"""get positions ->(size,avgp)"""
#TBD exec predtim?
def pos(self):
self.count()
return self.bt.get_open_position(symbol=self.symbol)
return self.bt.get_open_position(symbol=self.symbol, account=self.account)
"""get open orders ->list(Order)"""
def get_open_orders(self, side: OrderSide, symbol: str):
self.count()
return self.bt.get_open_orders(side=side, symbol=symbol)
return self.bt.get_open_orders(side=side, symbol=symbol, account=self.account)
def get_last_price(self, symbol: str):
self.count()
return self.bt.get_last_price(time=self.bt.time)
return self.bt.get_last_price(time=self.bt.time, account=self.account)

View File

@ -97,7 +97,7 @@ class LiveInterface(GeneralInterface):
return -1
"""sell limit"""
async def sell_l(self, price: float, size = 1, repeat: bool = False):
def sell_l(self, price: float, size = 1, repeat: bool = False):
self.size = size
self.repeat = repeat
@ -124,7 +124,7 @@ class LiveInterface(GeneralInterface):
return -1
"""order replace"""
async def repl(self, orderid: str, price: float = None, size: int = None, repeatl: bool = False):
def repl(self, orderid: str, price: float = None, size: int = None, repeatl: bool = False):
if not price and not size:
print("price or size has to be filled")

View File

@ -1,6 +1,7 @@
from threading import Thread
from threading import Thread, current_thread
from alpaca.trading.stream import TradingStream
from v2realbot.config import Keys
from v2realbot.common.model import Account
#jelikoz Alpaca podporuje pripojeni libovolneho poctu websocket instanci na order updates
#vytvorime pro kazdou bezici instanci vlastni webservisu (jinak bychom museli delat instanci pro kombinaci ACCOUNT1 - LIVE, ACCOUNT1 - PAPER, ACCOUNT2 - PAPER ..)
@ -14,15 +15,16 @@ As Alpaca supports connecting of any number of trade updates clients
new instance of this websocket thread is created for each strategy instance.
"""""
class LiveOrderUpdatesStreamer(Thread):
def __init__(self, key: Keys, name: str) -> None:
def __init__(self, key: Keys, name: str, account: Account) -> None:
self.key = key
self.account = account
self.strategy = None
self.client = TradingStream(api_key=key.API_KEY, secret_key=key.SECRET_KEY, paper=key.PAPER)
Thread.__init__(self, name=name)
#notif dispatcher - pouze 1 strategie
async def distributor(self,data):
if self.strategy.symbol == data.order.symbol: await self.strategy.order_updates(data)
if self.strategy.symbol == data.order.symbol: await self.strategy.order_updates(data, self.account)
# connects callback to interface object - responses for given symbol are routed to interface callback
def connect_callback(self, st):
@ -39,6 +41,6 @@ class LiveOrderUpdatesStreamer(Thread):
print("connect strategy first")
return
self.client.subscribe_trade_updates(self.distributor)
print("*"*10, "WS Order Update Streamer started for", self.strategy.name, "*"*10)
print("*"*10, "WS Order Update Streamer started for", current_thread().name,"*"*10)
self.client.run()

View File

@ -10,7 +10,7 @@ from fastapi.security import APIKeyHeader
import uvicorn
from uuid import UUID
from v2realbot.utils.ilog import get_log_window
from v2realbot.common.model import RunManagerRecord, StrategyInstance, RunnerView, RunRequest, Trade, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, Bar, RunArchiveChange, TestList, ConfigItem, InstantIndicator, DataTablesRequest, AnalyzerInputs
from v2realbot.common.model import RunManagerRecord, StrategyInstance, RunnerView, RunRequest, TradeView, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, Bar, RunArchiveChange, TestList, ConfigItem, InstantIndicator, DataTablesRequest, AnalyzerInputs
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Depends, HTTPException, status, WebSocketException, Cookie, Query, Request
from fastapi.responses import FileResponse, StreamingResponse, JSONResponse
from fastapi.staticfiles import StaticFiles
@ -334,7 +334,7 @@ def stop_all_runners():
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Error: {res}:{id}")
@app.get("/tradehistory/{symbol}", dependencies=[Depends(api_key_auth)])
def get_trade_history(symbol: str, timestamp_from: float, timestamp_to:float) -> list[Trade]:
def get_trade_history(symbol: str, timestamp_from: float, timestamp_to:float) -> list[TradeView]:
res, set = cs.get_trade_history(symbol, timestamp_from, timestamp_to)
if res == 0:
return set

View File

@ -12,7 +12,7 @@ import numpy as np
import v2realbot.controller.services as cs
from rich import print
from v2realbot.common.model import AnalyzerInputs
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
from pathlib import Path
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY

View File

@ -12,7 +12,7 @@ import numpy as np
import v2realbot.controller.services as cs
from rich import print
from v2realbot.common.model import AnalyzerInputs
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
from pathlib import Path
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY

View File

@ -11,7 +11,7 @@ import numpy as np
import v2realbot.controller.services as cs
from rich import print
from v2realbot.common.model import AnalyzerInputs
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
from pathlib import Path
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY

View File

@ -11,7 +11,7 @@ import numpy as np
import v2realbot.controller.services as cs
from rich import print
from v2realbot.common.model import AnalyzerInputs
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
from pathlib import Path
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY

View File

@ -11,7 +11,7 @@ import numpy as np
import v2realbot.controller.services as cs
from rich import print
from v2realbot.common.model import AnalyzerInputs
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
from pathlib import Path
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY

View File

@ -11,7 +11,7 @@ import numpy as np
import v2realbot.controller.services as cs
from rich import print
from v2realbot.common.model import AnalyzerInputs
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
from pathlib import Path
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY

View File

@ -11,7 +11,7 @@ import numpy as np
import v2realbot.controller.services as cs
from rich import print
from v2realbot.common.model import AnalyzerInputs
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
from pathlib import Path
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY

View File

@ -11,7 +11,7 @@ import numpy as np
import v2realbot.controller.services as cs
from rich import print
from v2realbot.common.model import AnalyzerInputs
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
from pathlib import Path
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY

View File

@ -12,7 +12,7 @@ import numpy as np
import v2realbot.controller.services as cs
from rich import print
from v2realbot.common.model import AnalyzerInputs
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
from pathlib import Path
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY

View File

@ -10,7 +10,7 @@ from enum import Enum
import numpy as np
import v2realbot.controller.services as cs
from rich import print
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
from pathlib import Path
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY

View File

@ -10,7 +10,7 @@ from enum import Enum
import numpy as np
import v2realbot.controller.services as cs
from rich import print
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
from pathlib import Path
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY

View File

@ -10,7 +10,7 @@ from enum import Enum
import numpy as np
import v2realbot.controller.services as cs
from rich import print
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
from pathlib import Path
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY

View File

@ -11,7 +11,7 @@ import numpy as np
import v2realbot.controller.services as cs
from rich import print
from v2realbot.common.model import AnalyzerInputs
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
from pathlib import Path
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
@ -23,7 +23,7 @@ from collections import defaultdict
from scipy.stats import zscore
from io import BytesIO
from typing import Tuple, Optional, List
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
def load_trades(runner_ids: List = None, batch_id: str = None) -> Tuple[int, List[Trade], int]:
if runner_ids is None and batch_id is None:

View File

@ -10,7 +10,7 @@ from enum import Enum
import numpy as np
import v2realbot.controller.services as cs
from rich import print
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get, print
from pathlib import Path
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY

View File

@ -4,7 +4,7 @@ from uuid import UUID, uuid4
from v2realbot.enums.enums import Moddus, SchedulerStatus, RecordType, StartBarAlign, Mode, Account, OrderSide
from v2realbot.common.model import RunManagerRecord, StrategyInstance, RunDay, StrategyInstance, Runner, RunRequest, RunArchive, RunArchiveView, RunArchiveViewPagination, RunArchiveDetail, RunArchiveChange, Bar, TradeEvent, TestList, Intervals, ConfigItem, InstantIndicator, DataTablesRequest, Market
from v2realbot.utils.utils import validate_and_format_time, AttributeDict, zoneNY, zonePRG, safe_get, dict_replace_value, Store, parse_toml_string, json_serial, is_open_hours, send_to_telegram, concatenate_weekdays, transform_data
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
from v2realbot.common.model import Trade, TradeDirection, TradeStatus, TradeStoplossType
from datetime import datetime
from v2realbot.config import JOB_LOG_FILE, STRATVARS_UNCHANGEABLES, ACCOUNT1_PAPER_API_KEY, ACCOUNT1_PAPER_SECRET_KEY, ACCOUNT1_LIVE_API_KEY, ACCOUNT1_LIVE_SECRET_KEY, DATA_DIR, MEDIA_DIRECTORY, RUNNER_DETAIL_DIRECTORY
import numpy as np

View File

@ -11,6 +11,44 @@ var markersLine = null
var avgBuyLine = null
var profitLine = null
var slLine = []
//create function which for each ACCOUNT1, ACCOUNT2 or ACCOUNT3 returns color for buy and color for sell - which can be strings representing color
//HELPERS FUNCTION - will go to utils
/**
* Returns an object containing the colors for buy and sell for the specified account.
*
* Parameters:
* account (string): The account for which to retrieve the colors (ACCOUNT1, ACCOUNT2, or ACCOUNT3).
*
* Returns:
* object: An object with 'buy' and 'sell' properties containing the corresponding color strings.
*
* Account 1:
#FF6B6B, #FF9999
Account 2:
#4ECDC4, #83E8E1
Account 3:
#FFD93D, #FFE787
Account 4:
#6C5CE7, #A29BFE
Another option for colors:
#1F77B4 (Entry) and #AEC7E8 (Exit)
#FF7F0E (Entry) and #FFBB78 (Exit)
#2CA02C (Entry) and #98DF8A (Exit)
#D62728 (Entry) and #FF9896 (Exit)
*/
function getAccountColors(account) {
const accountColors = {
ACCOUNT1: { accid: 'A1', buy: '#FF7F0E', sell: '#FFBB78' },
ACCOUNT2: { accid: 'A2',buy: '#1F77B4', sell: '#AEC7E8' },
ACCOUNT3: { accid: 'A3',buy: '#2CA02C', sell: '#98DF8A' },
ACCOUNT4: { accid: 'A4',buy: '#D62728', sell: '#FF9896' },
ACCOUNT5: { accid: 'A5',buy: 'purple', sell: 'orange' }
};
return accountColors[account] || { buy: '#37cade', sell: 'red' };
}
//TRANSFORM object returned from REST API get_arch_run_detail
//to series and markers required by lightweigth chart
//input array object bars = { high: [1,2,3], time: [1,2,3], close: [2,2,2]...}
@ -34,6 +72,11 @@ function transform_data(data) {
//cas of first record, nekdy jsou stejny - musim pridat setinku
prev_cas = 0
if ((data.ext_data !== null) && (data.ext_data.sl_history)) {
///sort sl_history according to order id string - i need all same order id together
data.ext_data.sl_history.sort(function (a, b) {
return a.id.localeCompare(b.id);
});
data.ext_data.sl_history.forEach((histRecord, index, array) => {
//console.log("plnime")
@ -48,6 +91,7 @@ function transform_data(data) {
//init nova sada
sl_line_sada = []
sl_line_markers_sada = []
sline_color = "#f5aa42"
}
prev_id = histRecord.id
@ -65,12 +109,21 @@ function transform_data(data) {
sline = {}
sline["time"] = cas
sline["value"] = histRecord.sl_val
if (histRecord.account) {
const accColors = getAccountColors(histRecord.account)
sline_color = histRecord.direction == "long" ? accColors.buy : accColors.sell //idealne
sline["color"] = sline_color
}
sl_line_sada.push(sline)
//ZDE JSEM SKONCIL
//COLOR SE NASTAVUJE V SERIES OPTIONS POZDEJI - nejak vymyslet
sline_markers = {}
sline_markers["time"] = cas
sline_markers["position"] = "inBar"
sline_markers["color"] = "#f5aa42"
sline_markers["color"] = sline_color
//sline_markers["shape"] = "circle"
//console.log("SHOW_SL_DIGITS",SHOW_SL_DIGITS)
sline_markers["text"] = SHOW_SL_DIGITS ? histRecord.sl_val.toFixed(3) : ""
@ -239,31 +292,33 @@ function transform_data(data) {
// //a_markers["text"] = CHART_SHOW_TEXT ? trade.position_qty + "/" + parseFloat(trade.pos_avg_price).toFixed(3) :trade.position_qty
// avgp_markers.push(a_markers)
}
}
}
const { accid: accountId,buy: buyColor, sell: sellColor } = getAccountColors(trade.account);
//buy sell markery
marker = {}
marker["time"] = timestamp;
// marker["position"] = (trade.order.side == "buy") ? "belowBar" : "aboveBar"
marker["position"] = (trade.order.side == "buy") ? "aboveBar" : "aboveBar"
marker["color"] = (trade.order.side == "buy") ? "#37cade" : "red"
marker["color"] = (trade.order.side == "buy") ? buyColor : sellColor
//marker["shape"] = (trade.order.side == "buy") ? "arrowUp" : "arrowDown"
marker["shape"] = (trade.order.side == "buy") ? "arrowUp" : "arrowDown"
//marker["text"] = trade.qty + "/" + trade.price
qt_optimized = (trade.order.qty % 1000 === 0) ? (trade.order.qty / 1000).toFixed(1) + 'K' : trade.order.qty
marker["text"] = accountId + " " //account shortcut
if (CHART_SHOW_TEXT) {
//včetně qty
//marker["text"] = qt_optimized + "@" + trade.price
//bez qty
marker["text"] = trade.price
marker["text"] += trade.price
closed_trade_marker_and_profit = (trade.profit) ? "c" + trade.profit.toFixed(1) + "/" + trade.profit_sum.toFixed(1) : "c"
marker["text"] += (trade.position_qty == 0) ? closed_trade_marker_and_profit : ""
} else {
closed_trade_marker_and_profit = (trade.profit) ? "c" + trade.profit.toFixed(1) + "/" + trade.profit_sum.toFixed(1) : "c"
marker["text"] = (trade.position_qty == 0) ? closed_trade_marker_and_profit : trade.price.toFixed(3)
marker["text"] += (trade.position_qty == 0) ? closed_trade_marker_and_profit : trade.price.toFixed(3)
}
markers.push(marker)
@ -844,7 +899,7 @@ function display_buy_markers(data) {
//console.log("uvnitr")
slLine_temp = chart.addLineSeries({
// title: "avgpbuyline",
color: '#e4c76d',
color: slRecord[0]["color"] ? slRecord[0]["color"] : '#e4c76d',
// color: 'transparent',
lineWidth: 1,
lastValueVisible: false

View File

@ -172,7 +172,7 @@ function initialize_archiveRecords() {
{
targets: [13,14,15],
render: function ( data, type, row ) {
return '<div class="tdsmall">'+data+'</div>'
return '<div class="tdsmall">'+JSON.stringify(data, null, 2)+'</div>'
},
},
{

View File

@ -4,7 +4,7 @@ from v2realbot.utils.tlog import tlog, tlog_exception
from v2realbot.enums.enums import Mode, Order, Account, RecordType, Followup
#from alpaca.trading.models import TradeUpdate
from v2realbot.common.model import TradeUpdate
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
from alpaca.trading.enums import TradeEvent, OrderStatus
from v2realbot.indicators.indicators import ema
import orjson
@ -44,7 +44,8 @@ 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)}"
printanyway(msg)
self.state.ilog(e=msg)
self.state.vars.pending = "max_sum_profit_to_quit_rel"
for account in self.accounts:
self.state.account_variables[account.name].pending = "max_sum_profit_to_quit_rel"
if self.mode not in [Mode.BT, Mode.PREP]:
send_to_telegram(msg)
if hard_cutoff:
@ -57,7 +58,8 @@ 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)}"
printanyway(msg)
self.state.ilog(e=msg)
self.state.vars.pending = "max_sum_loss_to_quit_rel"
for account in self.accounts:
self.state.account_variables[account.name].pending = "max_sum_loss_to_quit_rel"
if self.mode not in [Mode.BT, Mode.PREP]:
send_to_telegram(msg)
if hard_cutoff:
@ -71,7 +73,8 @@ 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)}"
printanyway(msg)
self.state.ilog(e=msg)
self.state.vars.pending = "max_sum_profit_to_quit"
for account in self.accounts:
self.state.account_variables[account.name].pending = "max_sum_profit_to_quit"
if self.mode not in [Mode.BT, Mode.PREP]:
send_to_telegram(msg)
if hard_cutoff:
@ -84,7 +87,8 @@ 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)}"
printanyway(msg)
self.state.ilog(e=msg)
self.state.vars.pending = "max_sum_loss_to_quit"
for account in self.accounts:
self.state.account_variables[account.name].pending = "max_sum_loss_to_quit"
if self.mode not in [Mode.BT, Mode.PREP]:
send_to_telegram(msg)
if hard_cutoff:
@ -95,9 +99,10 @@ class StrategyClassicSL(Strategy):
return False
async def add_followup(self, direction: TradeDirection, size: int, signal_name: str):
async def add_followup(self, direction: TradeDirection, size: int, signal_name: str, account: Account):
trade_to_add = Trade(
id=uuid4(),
account=account,
last_update=datetime.fromtimestamp(self.state.time).astimezone(zoneNY),
status=TradeStatus.READY,
size=size,
@ -108,45 +113,48 @@ class StrategyClassicSL(Strategy):
self.state.vars.prescribedTrades.append(trade_to_add)
self.state.vars.requested_followup = None
self.state.account_variables[account.name].requested_followup = None
self.state.ilog(e=f"FOLLOWUP {direction} added to prescr.trades {signal_name=} {size=}", trade=trade_to_add)
self.state.ilog(e=f"FOLLOWUP {direction} - {account} added to prescr.trades {signal_name=} {size=}", trade=trade_to_add)
async def orderUpdateBuy(self, data: TradeUpdate):
o: Order = data.order
signal_name = None
account = data.account
##nejak to vymyslet, aby se dal poslat cely Trade a serializoval se
self.state.ilog(e="Příchozí BUY notif", msg=o.status, trade=transform_data(data, json_serial))
self.state.ilog(e="Příchozí BUY notif"+account, msg=o.status, trade=transform_data(data, json_serial))
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
#jde o uzavření short pozice - počítáme PROFIT
if int(self.state.positions) < 0 or (int(self.state.positions) == 0 and self.state.wait_for_fill is not None):
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 data.event == TradeEvent.PARTIAL_FILL and self.state.wait_for_fill is None:
if data.event == TradeEvent.PARTIAL_FILL and self.state.account_variables[account.name].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
self.state.wait_for_fill = float(self.state.avgp)
self.state.account_variables[account.name].wait_for_fill = float(self.state.account_variables[account.name].avgp)
#PROFIT pocitame z TradeUpdate.price a TradeUpdate.qty - aktualne provedene mnozstvi a cena
#naklady vypocteme z prumerne ceny, kterou mame v pozicich
bought_amount = data.qty * data.price
#podle prumerne vstupni ceny, kolik stalo toto mnozstvi
if float(self.state.avgp) > 0:
vstup_cena = float(self.state.avgp)
elif float(self.state.avgp) == 0 and self.state.wait_for_fill is not None:
vstup_cena = float(self.state.wait_for_fill)
if float(self.state.account_variables[account.name].avgp) > 0:
vstup_cena = float(self.state.account_variables[account.name].avgp)
elif float(self.state.account_variables[account.name].avgp) == 0 and self.state.account_variables[account.name].wait_for_fill is not None:
vstup_cena = float(self.state.account_variables[account.name].wait_for_fill)
else:
vstup_cena = 0
avg_costs = vstup_cena * float(data.qty)
if avg_costs == 0:
self.state.ilog(e="ERR: 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"+account, msg="naklady=utrzena cena. TBD opravit.")
avg_costs = bought_amount
trade_profit = round((avg_costs-bought_amount),2)
self.state.profit += trade_profit
#celkovy profit
self.state.profit += trade_profit #overall abs profit
self.state.account_variables[account.name].profit += trade_profit #account profit
rel_profit = 0
#spoctene celkovy relativni profit za trade v procentech ((trade_profit/vstup_naklady)*100)
@ -160,30 +168,32 @@ class StrategyClassicSL(Strategy):
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
if data.position_qty != 0:
self.state.docasny_rel_profit.append(rel_profit)
self.state.account_variables[account.name].docasny_rel_profit.append(rel_profit)
partial_exit = True
else:
#jde o posledni z PARTIAL EXITU tzn.data.position_qty == 0
if len(self.state.docasny_rel_profit) > 0:
if len(self.state.account_variables[account.name].docasny_rel_profit) > 0:
#pricteme aktualni rel profit
self.state.docasny_rel_profit.append(rel_profit)
self.state.account_variables[account.name].docasny_rel_profit.append(rel_profit)
#a z rel profitu tohoto tradu vypocteme prumer, ktery teprve ulozime
rel_profit = round(np.mean(self.state.docasny_rel_profit),5)
self.state.docasny_rel_profit = []
rel_profit = round(np.mean(self.state.account_variables[account.name].docasny_rel_profit),5)
self.state.account_variables[account.name].docasny_rel_profit = []
partial_last = True
self.state.rel_profit_cum.append(rel_profit)
self.state.rel_profit_cum.append(rel_profit) #overall cum 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)
#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.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 - 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))
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))
#zapsat profit do prescr.trades
for trade in self.state.vars.prescribedTrades:
if trade.id == self.state.vars.pending:
if trade.id == self.state.account_variables[account.name].pending:
trade.last_update = datetime.fromtimestamp(self.state.time).astimezone(zoneNY)
trade.profit += trade_profit
#pro ulozeni do tradeData scitame vsechen zisk z tohoto tradu (kvuli partialum)
@ -201,7 +211,7 @@ class StrategyClassicSL(Strategy):
if data.event == TradeEvent.FILL:
#mazeme self.state.
self.state.wait_for_fill = None
self.state.account_variables[account.name].wait_for_fill = None
#zapsat update profitu do tradeList
for tradeData in self.state.tradeList:
if tradeData.execution_id == data.execution_id:
@ -209,7 +219,7 @@ class StrategyClassicSL(Strategy):
setattr(tradeData, "profit", trade_profit)
setattr(tradeData, "profit_sum", self.state.profit)
setattr(tradeData, "signal_name", signal_name)
setattr(tradeData, "prescribed_trade_id", self.state.vars.pending)
setattr(tradeData, "prescribed_trade_id", self.state.account_variables[account.name].pending)
#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_cum", rel_profit_cum_calculated)
@ -220,16 +230,16 @@ class StrategyClassicSL(Strategy):
#pIF REVERSAL REQUIRED - reverse position is added to prescr.Trades with same signal name
#jen při celém FILLU
if data.event == TradeEvent.FILL and self.state.vars.requested_followup is not None:
if self.state.vars.requested_followup == Followup.REVERSE:
await self.add_followup(direction=TradeDirection.LONG, size=o.qty, signal_name=signal_name)
elif self.state.vars.requested_followup == Followup.ADD:
if data.event == TradeEvent.FILL and self.state.account_variables[account.name].requested_followup is not None:
if self.state.account_variables[account.name].requested_followup == Followup.REVERSE:
await self.add_followup(direction=TradeDirection.LONG, size=o.qty, signal_name=signal_name, account=account)
elif self.state.account_variables[account.name].requested_followup == Followup.ADD:
#zatim stejna SIZE
await self.add_followup(direction=TradeDirection.SHORT, size=o.qty, signal_name=signal_name)
await self.add_followup(direction=TradeDirection.SHORT, size=o.qty, signal_name=signal_name, account=account)
else:
#zjistime nazev signalu a updatneme do tradeListu - abychom meli svazano
for trade in self.state.vars.prescribedTrades:
if trade.id == self.state.vars.pending:
if trade.id == self.state.account_variables[account.name].pending:
signal_name = trade.generated_by
#zapiseme entry_time (jen pokud to neni partial add) - tzn. jen poprvé
if data.event == TradeEvent.FILL and trade.entry_time is None:
@ -239,7 +249,7 @@ class StrategyClassicSL(Strategy):
for tradeData in self.state.tradeList:
if tradeData.execution_id == data.execution_id:
setattr(tradeData, "signal_name", signal_name)
setattr(tradeData, "prescribed_trade_id", self.state.vars.pending)
setattr(tradeData, "prescribed_trade_id", self.state.account_variables[account.name].pending)
self.state.ilog(e="BUY: Jde o LONG nakuú nepocitame profit zatim")
@ -248,43 +258,47 @@ class StrategyClassicSL(Strategy):
self.state.last_entry_price["long"] = data.price
#pokud neni nastaveno goal_price tak vyplnujeme defaultem
if self.state.vars.activeTrade.goal_price is None:
if self.state.account_variables[account.name].activeTrade.goal_price is None:
dat = dict(close=data.price)
self.state.vars.activeTrade.goal_price = get_profit_target_price(self.state, dat, TradeDirection.LONG)
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)
#ic("vstupujeme do orderupdatebuy")
print(data)
#dostavame zde i celkové akutální množství - ukládáme
self.state.positions = data.position_qty
self.state.avgp, self.state.positions = self.state.interface.pos()
self.state.account_variables[account.name].positions = data.position_qty
self.state.account_variables[account.name].avgp, self.state.account_variables[account.name].positions = self.state.interface[account.name].pos()
if o.status == OrderStatus.FILLED or o.status == OrderStatus.CANCELED:
#davame pryc pending
self.state.vars.pending = None
self.state.account_variables[account.name].pending = None
async def orderUpdateSell(self, data: TradeUpdate):
self.state.ilog(e="Příchozí SELL notif", msg=data.order.status, trade=transform_data(data, json_serial))
async def orderUpdateSell(self, data: TradeUpdate):
account = data.account
#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
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
#jde o uzavření long pozice - počítáme PROFIT
if int(self.state.positions) > 0 or (int(self.state.positions) == 0 and self.state.wait_for_fill is not None):
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 data.event == TradeEvent.PARTIAL_FILL and self.state.wait_for_fill is None:
if data.event == TradeEvent.PARTIAL_FILL and self.state.account_variables[account.name].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
self.state.wait_for_fill = float(self.state.avgp)
self.state.account_variables[account.name].wait_for_fill = float(self.state.account_variables[account.name].avgp)
#PROFIT pocitame z TradeUpdate.price a TradeUpdate.qty - aktualne provedene mnozstvi a cena
#naklady vypocteme z prumerne ceny, kterou mame v pozicich
sold_amount = data.qty * data.price
if float(self.state.avgp) > 0:
vstup_cena = float(self.state.avgp)
elif float(self.state.avgp) == 0 and self.state.wait_for_fill is not None:
vstup_cena = float(self.state.wait_for_fill)
if float(self.state.account_variables[account.name].avgp) > 0:
vstup_cena = float(self.state.account_variables[account.name].avgp)
elif float(self.state.account_variables[account.name].avgp) == 0 and self.state.account_variables[account.name].wait_for_fill is not None:
vstup_cena = float(self.state.account_variables[account.name].wait_for_fill)
else:
vstup_cena = 0
@ -292,12 +306,13 @@ class StrategyClassicSL(Strategy):
avg_costs = vstup_cena * float(data.qty)
if avg_costs == 0:
self.state.ilog(e="ERR: Nemame naklady na PROFIT, AVGP je nula. Zaznamenano jako 0", msg="naklady=utrzena cena. TBD opravit.")
self.state.ilog(e=f"ERR: {account} Nemame naklady na PROFIT, AVGP je nula. Zaznamenano jako 0", msg="naklady=utrzena cena. TBD opravit.")
avg_costs = sold_amount
trade_profit = round((sold_amount - avg_costs),2)
self.state.profit += trade_profit
self.state.profit += trade_profit #celkový profit
self.state.account_variables[account.name].profit += trade_profit #account specific profit
rel_profit = 0
#spoctene celkovy relativni profit za trade v procentech ((trade_profit/vstup_naklady)*100)
if vstup_cena != 0 and data.order.qty != 0:
@ -309,30 +324,31 @@ class StrategyClassicSL(Strategy):
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
if data.position_qty != 0:
self.state.docasny_rel_profit.append(rel_profit)
self.state.account_variables[account.name].docasny_rel_profit.append(rel_profit)
partial_exit = True
else:
#jde o posledni z PARTIAL EXITU tzn.data.position_qty == 0
if len(self.state.docasny_rel_profit) > 0:
if len(self.state.account_variables[account.name].docasny_rel_profit) > 0:
#pricteme aktualni rel profit
self.state.docasny_rel_profit.append(rel_profit)
self.state.account_variables[account.name].docasny_rel_profit.append(rel_profit)
#a z rel profitu tohoto tradu vypocteme prumer, ktery teprve ulozime
rel_profit = round(np.mean(self.state.docasny_rel_profit),5)
self.state.docasny_rel_profit = []
rel_profit = round(np.mean(self.state.account_variables[account.name].docasny_rel_profit),5)
self.state.account_variables[account.name].docasny_rel_profit = []
partial_last = True
self.state.rel_profit_cum.append(rel_profit)
self.state.rel_profit_cum.append(rel_profit) #overall 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)
#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.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 - 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 {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))
#zapsat profit do prescr.trades
for trade in self.state.vars.prescribedTrades:
if trade.id == self.state.vars.pending:
if trade.id == self.state.account_variables[account.name].pending:
trade.last_update = datetime.fromtimestamp(self.state.time).astimezone(zoneNY)
trade.profit += trade_profit
#pro ulozeni do tradeData scitame vsechen zisk z tohoto tradu (kvuli partialum)
@ -349,7 +365,7 @@ class StrategyClassicSL(Strategy):
if data.event == TradeEvent.FILL:
#mazeme self.state.
self.state.wait_for_fill = None
self.state.account_variables[account.name].wait_for_fill = None
#zapsat update profitu do tradeList
for tradeData in self.state.tradeList:
if tradeData.execution_id == data.execution_id:
@ -357,7 +373,7 @@ class StrategyClassicSL(Strategy):
setattr(tradeData, "profit", trade_profit)
setattr(tradeData, "profit_sum", self.state.profit)
setattr(tradeData, "signal_name", signal_name)
setattr(tradeData, "prescribed_trade_id", self.state.vars.pending)
setattr(tradeData, "prescribed_trade_id", self.state.account_variables[account.name].pending)
#self.state.ilog(f"updatnut tradeList o profi {str(tradeData)}")
setattr(tradeData, "rel_profit", rel_profit)
setattr(tradeData, "rel_profit_cum", rel_profit_cum_calculated)
@ -367,17 +383,17 @@ class StrategyClassicSL(Strategy):
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 data.event == TradeEvent.FILL and self.state.vars.requested_followup is not None:
if self.state.vars.requested_followup == Followup.REVERSE:
if data.event == TradeEvent.FILL and self.state.account_variables[account.name].requested_followup is not None:
if self.state.account_variables[account.name].requested_followup == Followup.REVERSE:
await self.add_followup(direction=TradeDirection.SHORT, size=data.order.qty, signal_name=signal_name)
elif self.state.vars.requested_followup == Followup.ADD:
elif self.state.account_variables[account.name].requested_followup == Followup.ADD:
#zatim stejna SIZE
await self.add_followup(direction=TradeDirection.LONG, size=data.order.qty, signal_name=signal_name)
else:
#zjistime nazev signalu a updatneme do tradeListu - abychom meli svazano
for trade in self.state.vars.prescribedTrades:
if trade.id == self.state.vars.pending:
if trade.id == self.state.account_variables[account.name].pending:
signal_name = trade.generated_by
#zapiseme entry_time (jen pokud to neni partial add) - tzn. jen poprvé
if data.event == TradeEvent.FILL and trade.entry_time is None:
@ -387,7 +403,7 @@ class StrategyClassicSL(Strategy):
for tradeData in self.state.tradeList:
if tradeData.execution_id == data.execution_id:
setattr(tradeData, "signal_name", signal_name)
setattr(tradeData, "prescribed_trade_id", self.state.vars.pending)
setattr(tradeData, "prescribed_trade_id", self.state.account_variables[account.name].pending)
self.state.ilog(e="SELL: Jde o SHORT nepocitame profit zatim")
@ -395,32 +411,32 @@ class StrategyClassicSL(Strategy):
#zapisujeme last entry price
self.state.last_entry_price["short"] = data.price
#pokud neni nastaveno goal_price tak vyplnujeme defaultem
if self.state.vars.activeTrade.goal_price is None:
if self.state.account_variables[account.name].activeTrade.goal_price is None:
dat = dict(close=data.price)
self.state.vars.activeTrade.goal_price = get_profit_target_price(self.state, dat, TradeDirection.SHORT)
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)
#sem v budoucnu dat i update SL
#if self.state.vars.activeTrade.stoploss_value is None:
#update pozic, v trade update je i pocet zbylych pozic
old_avgp = self.state.avgp
old_pos = self.state.positions
self.state.positions = int(data.position_qty)
old_avgp = self.state.account_variables[account.name].avgp
old_pos = self.state.account_variables[account.name].positions
self.state.account_variables[account.name].positions = int(data.position_qty)
if int(data.position_qty) == 0:
self.state.avgp = 0
self.state.account_variables[account.name].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.avgp, pos=self.state.positions, orderid=str(data.order.id))
#self.state.avgp, self.state.positions = self.interface.pos()
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.account_variables[account.name].avgp, self.state.account_variables[account.name].positions = self.interface.pos()
if data.event == TradeEvent.FILL or data.event == TradeEvent.CANCELED:
print("Příchozí SELL notifikace - complete FILL nebo CANCEL", data.event)
self.state.vars.pending = None
a,p = self.interface.pos()
self.state.account_variables[account.name].pending = None
a,p = self.interface[account.name].pos() #TBD maybe optimize for speed
#pri chybe api nechavame puvodni hodnoty
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
else: self.state.ilog(e=f"Chyba pri dotažení self.interface.pos() {a}")
#ic(self.state.avgp, self.state.positions)
#ic(self.state.account_variables[account.name].avgp, self.state.account_variables[account.name].positions)
#this parent method is called by strategy just once before waiting for first data
def strat_init(self):
@ -441,48 +457,47 @@ class StrategyClassicSL(Strategy):
else:
self.next(item, self.state)
#overidden methods
# pouziva se pri vstupu long nebo exitu short
# osetrit uzavreni s vice nez mam
def buy(self, size = None, repeat: bool = False):
def buy(self, account: Account, size = None, repeat: bool = False):
print("overriden buy method")
if size is None:
sizer = self.state.vars.chunk
else:
sizer = size
#jde o uzavreni short pozice
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.positions, size=size)
if int(self.state.account_variables[account.name].positions) < 0 and (int(self.state.account_variables[account.name].positions) + int(sizer)) > 0:
self.state.ilog(e="buy nelze nakoupit vic nez shortuji", positions=self.state.account_variables[account.name].positions, size=size)
printanyway("buy nelze nakoupit vic nez shortuji")
return -2
if int(self.state.positions) >= self.state.vars.maxpozic:
self.state.ilog(e="buy Maxim mnozstvi naplneno", positions=self.state.positions)
if int(self.state.account_variables[account.name].positions) >= self.state.vars.maxpozic:
self.state.ilog(e="buy Maxim mnozstvi naplneno", positions=self.state.account_variables[account.name].positions)
printanyway("max mnostvi naplneno")
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.bars['close'][-1])
return self.state.interface.buy(size=sizer)
return self.state.interface[account.name].buy(size=sizer)
#overidden methods
# pouziva se pri vstupu short nebo exitu long
def sell(self, size = None, repeat: bool = False):
def sell(self, account: Account, size = None, repeat: bool = False):
print("overriden sell method")
if size is None:
size = abs(int(self.state.positions))
size = abs(int(self.state.account_variables[account.name].positions))
#jde o uzavreni long pozice
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.positions, size=size)
if int(self.state.account_variables[account.name].positions) > 0 and (int(self.state.account_variables[account.name].positions) - int(size)) < 0:
self.state.ilog(e="nelze prodat vic nez longuji", positions=self.state.account_variables[account.name].positions, size=size)
printanyway("nelze prodat vic nez longuji")
return -2
#pokud shortuji a mam max pozic
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.positions, size=size)
if int(self.state.account_variables[account.name].positions) < 0 and abs(int(self.state.account_variables[account.name].positions)) >= self.state.vars.maxpozic:
self.state.ilog(e="short - Maxim mnozstvi naplneno", positions=self.state.account_variables[account.name].positions, size=size)
printanyway("short - Maxim mnozstvi naplneno")
return 0
@ -490,4 +505,4 @@ class StrategyClassicSL(Strategy):
#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.bars['close'][-1])
return self.state.interface.sell(size=size)
return self.state.interface[account.name].sell(size=size)

View File

@ -2,7 +2,7 @@
Strategy base class
"""
from datetime import datetime
from v2realbot.utils.utils import AttributeDict, zoneNY, is_open_rush, is_close_rush, json_serial, print
from v2realbot.utils.utils import AttributeDict, zoneNY, is_open_rush, is_close_rush, json_serial, print, gaka
from v2realbot.utils.tlog import tlog
from v2realbot.utils.ilog import insert_log, insert_log_multiple_queue
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.backtest_interface import BacktestInterface
from v2realbot.interfaces.live_interface import LiveInterface
import v2realbot.common.PrescribedTradeModel as ptm
import v2realbot.common.model as ptm
from alpaca.trading.enums import OrderSide
from v2realbot.backtesting.backtester import Backtester
#from alpaca.trading.models import TradeUpdate
from v2realbot.common.model import TradeUpdate
from v2realbot.common.model import TradeUpdate, AccountVariables
from alpaca.trading.enums import TradeEvent, OrderStatus
from threading import Event, current_thread
import orjson
@ -30,6 +30,7 @@ from collections import defaultdict
import v2realbot.strategyblocks.activetrade.sl.optimsl as optimsl
from tqdm import tqdm
import v2realbot.utils.config_handler as cfh
from typing import Dict, Set
if PROFILING_NEXT_ENABLED:
from pyinstrument import Profiler
@ -50,7 +51,8 @@ class Strategy:
self.rectype: RecordType = None
self.nextnew = 1
self.btdata: list = []
self.interface: GeneralInterface = None
self.interface: Dict[str, GeneralInterface] = {}
self.order_notifs: Dict[str, LiveOrderUpdatesStreamer] = {}
self.state: StrategyState = None
self.bt: Backtester = None
self.debug = False
@ -60,8 +62,8 @@ class Strategy:
self.open_rush = open_rush
self.close_rush = close_rush
self._streams = []
#primary account from runReqs
self.account = account
self.key = get_key(mode=self.mode, account=self.account)
self.rtqueue = None
self.runner_id = runner_id
self.ilog_save = ilog_save
@ -69,6 +71,9 @@ class Strategy:
self.secondary_res_start_index = dict()
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
self.q1 = queue.Queue()
self.q2 = queue.Queue()
@ -83,6 +88,25 @@ class Strategy:
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
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
#zatim jen jedna q1
#TODO zaroven strategie musi vedet o rectypu, protoze je zpracovava
@ -116,25 +140,33 @@ class Strategy:
return -1
self.debug = debug
self.key = get_key(mode=mode, account=self.account)
if mode == Mode.LIVE or mode == Mode.PAPER:
#data loader thread
self.dataloader = Trade_WS_Streamer(name="WS-LDR-"+self.name)
self.interface = LiveInterface(symbol=self.symbol, key=self.key)
# order notif thread
self.order_notifs = LiveOrderUpdatesStreamer(key=self.key, name="WS-STRMR-" + self.name)
#propojujeme notifice s interfacem (pro callback)
self.order_notifs.connect_callback(self)
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)
#populate interfaces for each account
for account in self.accounts:
#get key for account
key = get_key(mode=mode, account=Account(account))
self.interface[account.name] = LiveInterface(symbol=self.symbol, key=key)
# order notif thread
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:
self.dataloader = Trade_Offline_Streamer(start, end, btdata=self.btdata)
self.bt = Backtester(symbol = self.symbol, order_fill_callback= self.order_updates, btdata=self.btdata, cash=cash, bp_from=start, bp_to=end)
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.interface = BacktestInterface(symbol=self.symbol, bt=self.bt)
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)
#populate interfaces for each account
for account in self.accounts:
#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
##streamer bude plnit trady do listu trades - nad kterym bude pracovat paper trade
@ -142,10 +174,10 @@ class Strategy:
self.dataloader.add_stream(TradeAggregator2List(symbol=self.symbol,btdata=self.btdata,rectype=RecordType.TRADE))
elif mode == Mode.PREP:
#bt je zde jen pro udrzeni BT casu v logu atp. JInak jej nepouzivame.
self.bt = Backtester(symbol = self.symbol, order_fill_callback= self.order_updates, btdata=self.btdata, cash=cash, bp_from=start, bp_to=end)
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.interface = None
#self.interface = BacktestInterface(symbol=self.symbol, bt=self.bt)
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.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.order_notifs = None
else:
@ -314,13 +346,15 @@ class Strategy:
""""refresh positions and avgp - for CBAR once per confirmed, for BARS each time"""
def refresh_positions(self, item):
if self.rectype == RecordType.BAR:
a,p = self.interface.pos()
if a != -1:
self.state.avgp, self.state.positions = a,p
for account in self.accounts:
a,p = self.interface[account.name].pos()
if a != -1:
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:
a,p = self.interface.pos()
if a != -1:
self.state.avgp, self.state.positions = a,p
for account in self.accounts:
a,p = self.interface[account.name].pos()
if a != -1:
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"""
def update_times(self, item):
@ -416,7 +450,11 @@ class Strategy:
if self.mode == Mode.LIVE or self.mode == Mode.PAPER:
#live notification thread
self.order_notifs.start()
#for all keys in self.order_notifs call 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:
self.bt.backtest_start = datetime.now()
@ -486,7 +524,7 @@ class Strategy:
self.stop()
if self.mode == Mode.BT:
print("REQUEST COUNT:", self.interface.mincnt)
print("REQUEST COUNT:", {account_str:self.interface[account_str].mincnt for account_str in self.interface})
self.bt.backtest_end = datetime.now()
#print(40*"*",self.mode, "BACKTEST RESULTS",40*"*")
@ -500,7 +538,9 @@ class Strategy:
#disconnect strategy from websocket trader updates
if self.mode == Mode.LIVE or self.mode == Mode.PAPER:
self.order_notifs.disconnect_callback(self)
for key in self.order_notifs:
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)
for i in self._streams:
@ -541,11 +581,11 @@ class Strategy:
#for order updates from LIVE or BACKTEST
#updates are sent only for SYMBOL of strategy
async def order_updates(self, data: TradeUpdate):
async def order_updates(self, data: TradeUpdate, account: Account):
if self.mode == Mode.LIVE or self.mode == Mode.PAPER:
now = datetime.now().timestamp()
#z alpakýho TradeEvent si udelame svuj rozsireny TradeEvent (obsahujici navic profit atp.)
data = TradeUpdate(**data.dict())
data = TradeUpdate(**data.dict(), account=account)
else:
now = self.bt.time
@ -632,17 +672,14 @@ class Strategy:
rt_out["statinds"] = dict()
for key, value in self.state.statinds.items():
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
try:
sl_value = self.state.vars["activeTrade"].stoploss_value
sl_value = gaka(self.state.account_variables, "activeTrade", lambda x: x.stoploss_value)
except (KeyError, AttributeError):
sl_value = None
rt_out["positions"] = dict(time=self.state.time, positions=self.state.positions, avgp=self.state.avgp, sl_value=sl_value)
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)
#vkladame limitku a pendingbuys
try:
@ -718,17 +755,21 @@ class StrategyState:
"""Strategy Stat object that is passed to callbacks
note:
state.time
state.interface.time
state.interface[account.name].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é
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
"""
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):
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):
self.vars = stratvars
self.interface = interface
self.positions = 0
self.avgp = 0
self.blockbuy = 0
self.account = account #primary account
self.accounts = accounts
#populate account variables dictionary
self.account_variables: Dict[str, AccountVariables] = {account.name: AccountVariables() for account in self.accounts}
self.name = name
self.symbol = symbol
self.rectype = rectype
@ -737,11 +778,10 @@ class StrategyState:
self.time = None
#time of last trade processed
self.last_trade_time = 0
self.last_entry_price=dict(long=0,short=999)
self.last_entry_price={key:dict(long=0,short=999) for key in self.accounts}
self.resolution = None
self.runner_id = runner_id
self.bt = bt
self.dont_exit_already_activated = False
self.docasny_rel_profit = []
self.ilog_save = ilog_save
self.sl_optimizer_short = optimsl.SLOptimizer(ptm.TradeDirection.SHORT)
@ -779,18 +819,14 @@ class StrategyState:
#secondary resolution indicators
#self.secondary_indicators = AttributeDict(time=[], sec_price=[])
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.iter_log_list = []
self.dailyBars = defaultdict(dict)
#celkovy profit (prejmennovat na profit_cum)
self.profit = 0
self.profit = 0 #TODO key by account?
#celkovy relativni profit (obsahuje pole relativnich zisku, z jeho meanu se spocita celkovy rel_profit_cu,)
self.rel_profit_cum = []
self.rel_profit_cum = []#TODO key by account?
self.tradeList = []
#nova promenna pro externi data do ArchiveDetaili, napr. pro zobrazeni v grafu, je zde např. SL history
self.extData = defaultdict(dict)
@ -799,6 +835,25 @@ class StrategyState:
self.today_market_close = None
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):
#release large variables
self.bars = None

View File

@ -1,9 +1,13 @@
from v2realbot.strategyblocks.activetrade.sl.trailsl import trail_SL_management
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}
def manage_active_trade(state, data):
trade = state.vars.activeTrade
if trade is None:
return -1
trail_SL_management(state, data)
eval_close_position(state, data)
if len(accountsWithActiveTrade.values()) == 0:
return
trail_SL_management(state, accountsWithActiveTrade, data)
eval_close_position(state, accountsWithActiveTrade, data)

View File

@ -1,7 +1,7 @@
from v2realbot.strategy.base import StrategyState
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
from v2realbot.common.model 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.directive_utils import get_conditions_from_configuration
from v2realbot.common.model import SLHistory
@ -18,52 +18,61 @@ import os
from traceback import format_exc
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
def close_position(state, data, direction: TradeDirection, reason: str, followup: Followup = None):
def close_position(state: StrategyState, activeTrade: Trade, data, direction: TradeDirection, reason: str, followup: Followup = None):
followup_text = str(followup) if followup is not None else ""
state.ilog(lvl=1,e=f"CLOSING TRADE {followup_text} {reason} {str(direction)}", curr_price=data["close"], trade=state.vars.activeTrade)
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=activeTrade)
if direction == TradeDirection.SHORT:
res = state.buy(size=abs(int(state.positions)))
res = state.buy(account=activeTrade.account, size=abs(int(positions)))
if isinstance(res, int) and res < 0:
raise Exception(f"error in required operation {reason} {res}")
elif direction == TradeDirection.LONG:
res = state.sell(size=state.positions)
res = state.sell(account=activeTrade.account, size=positions)
if isinstance(res, int) and res < 0:
raise Exception(f"error in required operation STOPLOSS SELL {res}")
raise Exception(f"error in required operation STOPLOSS SELL {res}") #TBD error handling
else:
raise Exception(f"unknow TradeDirection in close_position")
#pri uzavreni tradu zapisujeme SL history - lepsi zorbazeni v grafu
insert_SL_history(state)
state.dont_exit_already_activated = False
state.vars.pending = state.vars.activeTrade.id
state.vars.activeTrade = None
state.vars.last_exit_index = data["index"]
insert_SL_history(state, activeTrade)
state.account_variables[activeTrade.account.name].pending = activeTrade.id
state.account_variables[activeTrade.account.name].activeTrade = None
#state.account_variables[activeTrade.account.name].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:
state.vars.requested_followup = followup
state.account_variables[activeTrade.account.name].requested_followup = followup
#close only partial position - no followup here, size multiplier must be between 0 and 1
def close_position_partial(state, data, direction: TradeDirection, reason: str, size: float):
def close_position_partial(state, activeTrade: Trade,data, direction: TradeDirection, reason: str, size: float):
positions = state.account_variables[activeTrade.account.name].positions
if size <= 0 or size >=1:
raise Exception(f"size must be betweem 0 and 1")
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=state.vars.activeTrade)
size_abs = abs(int(int(positions)*size))
state.ilog(lvl=1,e=f"CLOSING TRADE PART: {size_abs} {size} {reason} {str(direction)}", curr_price=data["close"], trade=activeTrade)
if direction == TradeDirection.SHORT:
res = state.buy(size=size_abs)
res = state.buy(account=activeTrade.account, size=size_abs)
if isinstance(res, int) and res < 0:
raise Exception(f"error in required operation STOPLOSS PARTIAL BUY {reason} {res}")
elif direction == TradeDirection.LONG:
res = state.sell(size=size_abs)
res = state.sell(account=activeTrade.account, size=size_abs)
if isinstance(res, int) and res < 0:
raise Exception(f"error in required operation STOPLOSS PARTIAL SELL {res}")
else:
raise Exception(f"unknow TradeDirection in close_position")
#pri uzavreni tradu zapisujeme SL history - lepsi zorbazeni v grafu
insert_SL_history(state)
state.vars.pending = state.vars.activeTrade.id
insert_SL_history(state, activeTrade)
state.account_variables[activeTrade.account.name].pending = 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.last_exit_index = data["index"]
state.vars.last_exit_index = data["index"] #ponechano mimo account

View File

@ -1,7 +1,7 @@
from v2realbot.strategy.base import StrategyState
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
from v2realbot.common.model 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.directive_utils import get_conditions_from_configuration
from v2realbot.common.model import SLHistory
@ -12,9 +12,9 @@ from threading import Event
import os
from traceback import format_exc
from v2realbot.strategyblocks.indicators.helpers import evaluate_directive_conditions
from v2realbot.strategyblocks.activetrade.helpers import get_override_for_active_trade, normalize_tick
from v2realbot.strategyblocks.activetrade.helpers import get_signal_section_directive, normalize_tick
def dontexit_protection_met(state, data, direction: TradeDirection):
def dontexit_protection_met(state, activeTrade: Trade, data, direction: TradeDirection):
if direction == TradeDirection.LONG:
smer = "long"
else:
@ -24,58 +24,64 @@ def dontexit_protection_met(state, data, direction: TradeDirection):
#vyreseno pri kazde aktivaci se vyplni flag already_activated
#pri naslednem false podminky se v pripade, ze je aktivovany flag posle True -
#take se vyrusi v closu
def process_result(result):
def process_result(result, account):
if result:
state.dont_exit_already_activated = True
state.account_variables[account.name].dont_exit_already_activated = True
return True
else:
return False
def evaluate_result():
mother_signal = state.vars.activeTrade.generated_by
mother_signal = activeTrade.generated_by
dont_exit_already_activated = state.account_variables[activeTrade.account.name].dont_exit_already_activated
if mother_signal is not None:
#TESTUJEME DONT_EXIT_
cond_dict = state.vars.conditions[KW.dont_exit][mother_signal][smer]
#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(state.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(dont_exit_already_activated))
if result:
return True
#OR neprosly testujeme 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(state.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(dont_exit_already_activated))
if result:
return True
cond_dict = state.vars.conditions[KW.dont_exit]["common"][smer]
#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(state.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(dont_exit_already_activated))
if result:
return True
#OR neprosly testujeme 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(state.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(dont_exit_already_activated))
return result
#nejprve evaluujeme vsechny podminky
result = evaluate_result()
#pak evaluujeme vysledek a vracíme
return process_result(result)
return process_result(result, activeTrade.account)
def exit_conditions_met(state, data, direction: TradeDirection):
def exit_conditions_met(state: StrategyState, activeTrade: Trade, data, direction: TradeDirection):
if direction == TradeDirection.LONG:
smer = "long"
else:
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"
exit_cond_only_on_confirmed = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
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))
if exit_cond_only_on_confirmed and data['confirmed'] == 0:
state.ilog(lvl=0,e="EXIT COND ONLY ON CONFIRMED BAR")
@ -83,20 +89,20 @@ def exit_conditions_met(state, data, direction: TradeDirection):
## minimální počet barů od vstupu
directive_name = "exit_cond_req_bars"
exit_cond_req_bars = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, 1))
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))
if state.vars.last_in_index is not None:
index_to_compare = int(state.vars.last_in_index)+int(exit_cond_req_bars)
if last_entry_index is not None:
index_to_compare = int(last_entry_index)+int(exit_cond_req_bars)
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_in_index=state.vars.last_in_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_entry_index=last_entry_index)
return False
#POKUD je nastaven MIN PROFIT, zkontrolujeme ho a az pripadne pustime CONDITIONY
directive_name = "exit_cond_min_profit"
exit_cond_min_profit_nodir = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
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))
directive_name = "exit_cond_min_profit_" + str(smer)
exit_cond_min_profit = get_override_for_active_trade(state, directive_name=directive_name, default_value=exit_cond_min_profit_nodir)
exit_cond_min_profit = get_signal_section_directive(state, signal_name=signal_name,directive_name=directive_name, default_value=exit_cond_min_profit_nodir)
#máme nastavený exit_cond_min_profit
@ -105,10 +111,10 @@ def exit_conditions_met(state, data, direction: TradeDirection):
if exit_cond_min_profit is not None:
exit_cond_min_profit_normalized = normalize_tick(state, data, float(exit_cond_min_profit))
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)
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)
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=}")
if (int(state.positions) < 0 and curr_price<=exit_cond_goal_price) or (int(state.positions) > 0 and curr_price>=exit_cond_goal_price):
if (int(positions) < 0 and curr_price<=exit_cond_goal_price) or (int(positions) > 0 and curr_price>=exit_cond_goal_price):
state.ilog(lvl=1,e=f"EXIT COND min profit PASS - POKRACUJEME")
else:
state.ilog(lvl=1,e=f"EXIT COND min profit NOT PASS")
@ -137,10 +143,10 @@ def exit_conditions_met(state, data, direction: TradeDirection):
#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])
mother_signal = state.vars.activeTrade.generated_by
mother_signal = signal_name
if mother_signal is not None:
cond_dict = state.vars.conditions[KW.exit][state.vars.activeTrade.generated_by][smer]
cond_dict = state.vars.conditions[KW.exit][signal_name][smer]
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)
if result:

View File

@ -1,7 +1,7 @@
from v2realbot.strategy.base import StrategyState
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
from v2realbot.common.model 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.directive_utils import get_conditions_from_configuration
from v2realbot.common.model import SLHistory
@ -18,10 +18,10 @@ import os
from traceback import format_exc
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.helpers import get_max_profit_price, get_profit_target_price, get_override_for_active_trade, keyword_conditions_met
from v2realbot.strategyblocks.activetrade.helpers import get_max_profit_price, get_profit_target_price, get_signal_section_directive
def eod_exit_activated(state: StrategyState, data, direction: TradeDirection):
def eod_exit_activated(state: StrategyState, activeTrade: Trade, data, direction: TradeDirection):
"""
Function responsible for end of day management
@ -38,8 +38,10 @@ def eod_exit_activated(state: StrategyState, data, direction: TradeDirection):
- 1 min forced immediate
"""
avgp = state.account_variables[activeTrade.account.name].avgp
directive_name = "forced_exit_window_start"
forced_exit_window_start = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
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))
if forced_exit_window_start is None:
state.ilog(lvl=0,e="Forced exit not required.")
@ -47,7 +49,7 @@ def eod_exit_activated(state: StrategyState, data, direction: TradeDirection):
directive_name = "forced_exit_window_end"
forced_exit_window_end = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, 389))
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))
if forced_exit_window_start>389:
state.ilog(lvl=0,e="Forced exit window end max is 389")
@ -60,7 +62,7 @@ def eod_exit_activated(state: StrategyState, data, direction: TradeDirection):
# #dokdy konci okno snizujiciho se profitu (zbytek je breakeven a posledni minuta forced) - default pulka okna
# directive_name = "forced_exit_decreasing_profit_window_end"
# 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))
# 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))
# 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.")
@ -72,7 +74,7 @@ def eod_exit_activated(state: StrategyState, data, direction: TradeDirection):
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"
forced_exit_breakeven_period = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, True))
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))
if forced_exit_breakeven_period is False:
return False
@ -80,11 +82,11 @@ def eod_exit_activated(state: StrategyState, data, direction: TradeDirection):
#zatim krom posledni minuty cekame alespon na breakeven
curr_price = float(data['close'])
#short smer
if direction == TradeDirection.SHORT and curr_price<=float(state.avgp):
if direction == TradeDirection.SHORT and curr_price<=float(avgp):
state.ilog(lvl=1,e=f"Forced Exit - price better than avgp, dir SHORT")
return True
if direction == TradeDirection.LONG and curr_price>=float(state.avgp):
if direction == TradeDirection.LONG and curr_price>=float(avgp):
state.ilog(lvl=1,e=f"Forced Exit - price better than avgp, dir LONG")
return True

View File

@ -1,198 +1,207 @@
from v2realbot.strategyblocks.activetrade.close.close_position import close_position, close_position_partial
from v2realbot.strategy.base import StrategyState
from v2realbot.enums.enums import Followup
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
from v2realbot.utils.utils import safe_get
from v2realbot.config import KW
#from icecream import install, ic
from rich import print as printanyway
from threading import Event
#import gaka
from v2realbot.utils.utils import gaka
import os
from traceback import format_exc
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.helpers import get_max_profit_price, get_profit_target_price, get_override_for_active_trade, keyword_conditions_met
from v2realbot.strategyblocks.activetrade.helpers import get_max_profit_price, get_profit_target_price, get_signal_section_directive, keyword_conditions_met
from v2realbot.strategyblocks.activetrade.sl.optimsl import SLOptimizer
def eval_close_position(state: StrategyState, data):
#TODO tady odsud
def eval_close_position(state: StrategyState, accountsWithActiveTrade, data):
curr_price = float(data['close'])
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))
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")))
if int(state.positions) != 0 and float(state.avgp)>0 and state.vars.pending is None:
#close position handling
#TBD pridat OPTIMALIZACI POZICE - EXIT 1/2
#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}")
#iterate over accountsWithActiveTrade
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
if int(positions) != 0 and float(avgp)>0 and pending is None:
#SL OPTIMALIZATION - PARTIAL EXIT
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:
#close position handling
#TBD pridat OPTIMALIZACI POZICE - EXIT 1/2
directive_name = 'reverse_for_SL_exit_short'
reverse_for_SL_exit = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, "no"))
#mame short pozice - (IDEA: rozlisovat na zaklade aktivniho tradu - umozni mi spoustet i pri soucasne long pozicemi)
if int(positions) < 0:
#get TARGET PRICE pro dany smer a signal
if reverse_for_SL_exit == "always":
followup_action = Followup.REVERSE
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
#pokud existujeme bereme z nastaveni tradu a nebo z defaultu
if activeTrade.goal_price is not None:
goal_price = activeTrade.goal_price
else:
followup_action = None
close_position(state=state, data=data, direction=TradeDirection.SHORT, reason="SL REACHED", followup=followup_action)
return
goal_price = get_profit_target_price(state, data, activeTrade, TradeDirection.SHORT)
max_price = get_max_profit_price(state, activeTrade, data, TradeDirection.SHORT)
state.ilog(lvl=1,e=f"Def Goal price {str(TradeDirection.SHORT)} {goal_price} max price {max_price}")
#REVERSE BASED ON REVERSE CONDITIONS
if keyword_conditions_met(state, data, direction=TradeDirection.SHORT, keyword=KW.reverse):
close_position(state=state, data=data, direction=TradeDirection.SHORT, reason="REVERSE COND MET", followup=Followup.REVERSE)
return
#SL OPTIMALIZATION - PARTIAL EXIT
level_met, exit_adjustment = state.sl_optimizer_short.eval_position(state, data, activeTrade)
if level_met is not None and exit_adjustment is not None:
position = 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, 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))
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:
#EXIT ADD CONDITIONS MET (exit and add)
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
directive_name = 'reverse_for_SL_exit_short'
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"))
#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:
if reverse_for_SL_exit == "always":
followup_action = Followup.REVERSE
elif add_for_cond_exit_short:
followup_action = Followup.ADD
elif reverse_for_SL_exit == "cond":
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
else:
followup_action = None
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=}")
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.SHORT, reason="SL REACHED", followup=followup_action)
return
#pokud je cena horsi, ale byla uz dont exit aktivovany - pak prodavame také
elif state.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, data=data, direction=TradeDirection.SHORT, reason=f"EXIT PROTECTION BOUNCE {state.dont_exit_already_activated=}")
state.dont_exit_already_activated = False
return
#REVERSE BASED ON REVERSE CONDITIONS
if keyword_conditions_met(state, data, activeTrade=activeTrade, direction=TradeDirection.SHORT, keyword=KW.reverse):
close_position(state=state, activeTrade=activeTrade,data=data, direction=TradeDirection.SHORT, reason="REVERSE COND MET", followup=Followup.REVERSE)
return
#FORCED EXIT PRI KONCI DNE
if eod_exit_activated(state, data, TradeDirection.SHORT):
close_position(state=state, data=data, direction=TradeDirection.SHORT, reason="EOD EXIT ACTIVATED")
return
#mame long
elif int(state.positions) > 0:
#EXIT ADD CONDITIONS MET (exit and add)
if keyword_conditions_met(state, data, activeTrade=activeTrade, direction=TradeDirection.SHORT, keyword=KW.exitadd):
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.SHORT, reason="EXITADD COND MET", followup=Followup.ADD)
return
#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.LONG)
max_price = get_max_profit_price(state, data, TradeDirection.LONG)
state.ilog(lvl=1,e=f"Goal price {str(TradeDirection.LONG)} {goal_price} max price {max_price}")
#CLOSING BASED ON EXIT CONDITIONS
if exit_conditions_met(state, activeTrade, data, TradeDirection.SHORT):
directive_name = 'reverse_for_cond_exit_short'
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))
directive_name = 'add_for_cond_exit_short'
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))
if reverse_for_cond_exit_short:
followup_action = Followup.REVERSE
elif add_for_cond_exit_short:
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
#SL OPTIMALIZATION - PARTIAL EXIT
level_met, exit_adjustment = state.sl_optimizer_long.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.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))
printanyway(f"SL OPTIMIZATION ENGAGED {str(TradeDirection.LONG)} {position=} {level_met=} {exit_adjustment}")
close_position_partial(state=state, data=data, direction=TradeDirection.LONG, reason=f"SL OPT LEVEL {level_met} REACHED", size=exit_adjustment)
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, activeTrade=activeTrade, data=data,direction=TradeDirection.SHORT) is False:
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
#SL FULL execution
if curr_price < state.vars.activeTrade.stoploss_value:
directive_name = 'reverse_for_SL_exit_long'
reverse_for_SL_exit = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, "no"))
#FORCED EXIT PRI KONCI DNE
if eod_exit_activated(state, activeTrade=activeTrade, data=data, direction=TradeDirection.SHORT):
close_position(state=state, activeTrade=activeTrade,data=data, direction=TradeDirection.SHORT, reason="EOD EXIT ACTIVATED")
return
#mame long
elif int(positions) > 0:
state.ilog(lvl=1, e=f"reverse_for_SL_exit {reverse_for_SL_exit}")
if reverse_for_SL_exit == "always":
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
#get TARGET PRICE pro dany smer a signal
#pokud existujeme bereme z nastaveni tradu a nebo z defaultu
if activeTrade.goal_price is not None:
goal_price = activeTrade.goal_price
else:
followup_action = None
close_position(state=state, data=data, direction=TradeDirection.LONG, reason="SL REACHED", followup=followup_action)
return
goal_price = get_profit_target_price(state, data, activeTrade, TradeDirection.LONG)
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}")
#REVERSE BASED ON REVERSE CONDITIONS
if keyword_conditions_met(state, data,TradeDirection.LONG, KW.reverse):
close_position(state=state, data=data, direction=TradeDirection.LONG, reason="REVERSE COND MET", followup=Followup.REVERSE)
return
#SL OPTIMALIZATION - PARTIAL EXIT
level_met, exit_adjustment = state.sl_optimizer_long.eval_position(state, data, activeTrade)
if level_met is not None and exit_adjustment is not None:
position = positions * exit_adjustment
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
#EXIT ADD CONDITIONS MET (exit and add)
if keyword_conditions_met(state, data, TradeDirection.LONG, KW.exitadd):
close_position(state=state, data=data, direction=TradeDirection.LONG, reason="EXITADD COND MET", followup=Followup.ADD)
return
#SL FULL execution
if curr_price < activeTrade.stoploss_value:
directive_name = 'reverse_for_SL_exit_long'
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"))
#EXIT CONDITIONS
if exit_conditions_met(state, data, TradeDirection.LONG):
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:
state.ilog(lvl=1, e=f"reverse_for_SL_exit {reverse_for_SL_exit}")
if reverse_for_SL_exit == "always":
followup_action = Followup.REVERSE
elif add_for_cond_exit_long:
followup_action = Followup.ADD
elif reverse_for_SL_exit == "cond":
followup_action = Followup.REVERSE if keyword_conditions_met(state, data, activeTrade, direction=TradeDirection.LONG, keyword=KW.slreverseonly, skip_conf_validation=True) else None
else:
followup_action = None
close_position(state=state, 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, data, direction=TradeDirection.LONG) is False:
close_position(state=state, data=data, direction=TradeDirection.LONG, reason=f"PROFIT or MAXPROFIT REACHED {max_price_signal=}")
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.LONG, reason="SL REACHED", followup=followup_action)
return
#pokud je cena horsi, ale byl uz dont exit aktivovany - pak prodavame také
elif state.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, data=data, direction=TradeDirection.LONG, reason=f"EXIT PROTECTION BOUNCE {state.dont_exit_already_activated=}")
state.dont_exit_already_activated = False
return
#FORCED EXIT PRI KONCI DNE
if eod_exit_activated(state, data, TradeDirection.LONG):
close_position(state=state, data=data, direction=TradeDirection.LONG, reason="EOD EXIT ACTIVATED")
return
#REVERSE BASED ON REVERSE CONDITIONS
if keyword_conditions_met(state, data, activeTrade, TradeDirection.LONG, KW.reverse):
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.LONG, reason="REVERSE COND MET", followup=Followup.REVERSE)
return
#EXIT ADD CONDITIONS MET (exit and add)
if keyword_conditions_met(state, data, activeTrade, TradeDirection.LONG, KW.exitadd):
close_position(state=state, activeTrade=activeTrade, data=data, direction=TradeDirection.LONG, reason="EXITADD COND MET", followup=Followup.ADD)
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

View File

@ -1,5 +1,5 @@
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
from v2realbot.common.model 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.directive_utils import get_conditions_from_configuration
from v2realbot.common.model import SLHistory
@ -18,17 +18,20 @@ from traceback import format_exc
from v2realbot.strategyblocks.helpers import normalize_tick
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)
def keyword_conditions_met(state, data, direction: TradeDirection, keyword: KW, skip_conf_validation: bool = False):
def keyword_conditions_met(state, data, activeTrade: Trade, direction: TradeDirection, keyword: KW, skip_conf_validation: bool = False):
action = str(keyword).upper()
if direction == TradeDirection.LONG:
smer = "long"
else:
smer = "short"
mother_signal = activeTrade.generated_by
if skip_conf_validation is False:
directive_name = "exit_cond_only_on_confirmed"
exit_cond_only_on_confirmed = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
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))
if exit_cond_only_on_confirmed and data['confirmed'] == 0:
state.ilog(lvl=0,e=f"{action} CHECK COND ONLY ON CONFIRMED BAR")
@ -37,7 +40,7 @@ def keyword_conditions_met(state, data, direction: TradeDirection, keyword: KW,
#TOTO zatim u REVERSU neresime
# #POKUD je nastaven MIN PROFIT, zkontrolujeme ho a az pripadne pustime CONDITIONY
# directive_name = "exit_cond_min_profit"
# exit_cond_min_profit = get_override_for_active_trade(directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
# exit_cond_min_profit = get_signal_section_directive(directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
# #máme nastavený exit_cond_min_profit
# # zjistíme, zda jsme v daném profit a případně nepustíme dál
@ -77,8 +80,6 @@ def keyword_conditions_met(state, data, direction: TradeDirection, keyword: KW,
#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])
mother_signal = state.vars.activeTrade.generated_by
if mother_signal is not None:
cond_dict = state.vars.conditions[keyword][mother_signal][smer]
result, conditions_met = evaluate_directive_conditions(state, cond_dict, "OR")
@ -108,12 +109,12 @@ def keyword_conditions_met(state, data, direction: TradeDirection, keyword: KW,
#mozna do SL helpers tuto
def insert_SL_history(state):
def insert_SL_history(state, activeTrade: Trade):
#insert stoploss history as key sl_history into runner archive extended data
state.extData["sl_history"].append(SLHistory(id=state.vars.activeTrade.id, time=state.time, sl_val=state.vars.activeTrade.stoploss_value))
state.extData["sl_history"].append(SLHistory(id=activeTrade.id, time=state.time, sl_val=activeTrade.stoploss_value, direction=activeTrade.direction, account=activeTrade.account))
def get_default_sl_value(state, direction: TradeDirection):
def get_default_sl_value(state, signal_name, direction: TradeDirection):
if direction == TradeDirection.LONG:
smer = "long"
@ -128,15 +129,16 @@ def get_default_sl_value(state, direction: TradeDirection):
state.ilog(lvl=1,e="No options for exit in stratvars. Fallback.")
return 0.01
directive_name = 'SL_defval_'+str(smer)
val = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(options, directive_name, 0.01))
val = get_signal_section_directive(state, signal_name, directive_name=directive_name, default_value=safe_get(options, directive_name, 0.01))
return val
#funkce pro direktivy, ktere muzou byt overridnute v signal sekci
#tato funkce vyhleda signal sekci aktivniho tradu a pokusi se danou direktivu vyhledat tam,
#pokud nenajde tak vrati default, ktery byl poskytnut
def get_override_for_active_trade(state, directive_name: str, default_value: str):
#TODO toto predelat na jiny nazev get_overide_for_directive_section (vstup muze byt opuze signal_name)
def get_signal_section_directive(state, signal_name: str, directive_name: str, default_value: str):
val = default_value
override = "NO"
mother_signal = state.vars.activeTrade.generated_by
mother_signal = signal_name
if mother_signal is not None:
override = "YES "+mother_signal
@ -145,30 +147,30 @@ def get_override_for_active_trade(state, directive_name: str, default_value: str
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
def get_profit_target_price(state, data, direction: TradeDirection):
def get_profit_target_price(state, data, activeTrade, direction: TradeDirection):
if direction == TradeDirection.LONG:
smer = "long"
else:
smer = "short"
directive_name = "profit"
def_profit_both_directions = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, 0.50))
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))
#profit pro dany smer
directive_name = 'profit_'+str(smer)
def_profit = get_override_for_active_trade(state, directive_name=directive_name, default_value=def_profit_both_directions)
def_profit = get_signal_section_directive(state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=def_profit_both_directions)
#mame v direktivve ticky
if isinstance(def_profit, (float, int)):
to_return = get_normalized_profitprice_from_tick(state, data, def_profit, direction)
to_return = get_normalized_profitprice_from_tick(state, data, def_profit, activeTrade.account, direction)
#mame v direktive indikator
elif isinstance(def_profit, str):
to_return = float(value_or_indicator(state, def_profit))
#min profit (ochrana extremnich hodnot indikatoru)
directive_name = 'profit_min_ind_tick_value'
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, direction)
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_price_value = get_normalized_profitprice_from_tick(state, data, profit_min_ind_tick_value, activeTrade.account, direction)
#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:
@ -179,28 +181,32 @@ def get_profit_target_price(state, data, direction: TradeDirection):
return to_return
##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, direction: TradeDirection):
def get_normalized_profitprice_from_tick(state, data, tick, account: Account, direction: TradeDirection):
avgp = state.account_variables[account.name].avgp
normalized_tick = normalize_tick(state, data, float(tick))
base_price = state.avgp if state.avgp != 0 else data["close"]
base_price = avgp if 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)
state.ilog(lvl=0,e=f"NORMALIZED TICK {tick=} {normalized_tick=} NORM.PRICE {returned_price}")
return returned_price
def get_max_profit_price(state, data, direction: TradeDirection):
def get_max_profit_price(state, activeTrade: Trade, data, direction: TradeDirection):
if direction == TradeDirection.LONG:
smer = "long"
else:
smer = "short"
directive_name = "max_profit"
max_profit_both_directions = get_override_for_active_trade(state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, 0.35))
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))
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
directive_name = 'max_profit_'+str(smer)
max_profit = get_override_for_active_trade(state, directive_name=directive_name, default_value=max_profit_both_directions)
max_profit = get_signal_section_directive(state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=max_profit_both_directions)
normalized_max_profit = normalize_tick(state,data,float(max_profit))
state.ilog(lvl=0,e=f"MAX PROFIT {max_profit=} {normalized_max_profit=}")
return price2dec(float(state.avgp)+normalized_max_profit,3) if int(state.positions) > 0 else price2dec(float(state.avgp)-normalized_max_profit,3)
return price2dec(float(avgp)+normalized_max_profit,3) if int(positions) > 0 else price2dec(float(avgp)-normalized_max_profit,3)

View File

@ -1,8 +1,8 @@
import numpy as np
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
from typing import Tuple
from copy import deepcopy
from v2realbot.strategyblocks.activetrade.helpers import get_override_for_active_trade
from v2realbot.strategyblocks.activetrade.helpers import get_signal_section_directive
from v2realbot.utils.utils import safe_get
# FIBONACCI PRO PROFIT A SL
@ -49,23 +49,23 @@ class SLOptimizer:
# self.exit_levels = self.init_exit_levels
# self.exit_sizes = self.init_exit_sizes
def get_trade_details(self, state):
trade: Trade = state.vars.activeTrade
def get_trade_details(self, state, activeTrade):
trade: Trade = activeTrade
#jde o novy trade - resetujeme levely
if trade.id != self.last_trade:
#inicializujeme a vymazeme pripadne puvodni
if self.initialize_levels(state) is False:
if self.initialize_levels(state, activeTrade) is False:
return None, None
self.last_trade = trade.id
#return cost_price, sl_price
return state.avgp, trade.stoploss_value
return state.account_variables[trade.account.name].avgp, trade.stoploss_value
def initialize_levels(self, state):
def initialize_levels(self, state, activeTrade):
directive_name = 'SL_opt_exit_levels_'+str(self.direction.value)
SL_opt_exit_levels = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
SL_opt_exit_levels = get_signal_section_directive(state=state, signal_name=activeTrade.generated_by, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
directive_name = 'SL_opt_exit_sizes_'+str(self.direction.value)
SL_opt_exit_sizes = get_override_for_active_trade(state=state, directive_name=directive_name, default_value=safe_get(state.vars, directive_name, None))
SL_opt_exit_sizes = get_signal_section_directive(state=state, signal_name=activeTrade.generated_by, 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:
#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=}")
return True
def get_initial_abs_levels(self, state):
def get_initial_abs_levels(self, state, activeTrade):
"""
Returns price levels corresponding to initial setting of exit_levels
"""
cost_price, sl_price = self.get_trade_details(state)
cost_price, sl_price = self.get_trade_details(state, activeTrade)
if cost_price is None or sl_price is None:
return []
curr_sl_distance = np.abs(cost_price - sl_price)
@ -96,11 +96,11 @@ class SLOptimizer:
else:
return [cost_price - exit_level * curr_sl_distance for exit_level in self.init_exit_levels]
def get_remaining_abs_levels(self, state):
def get_remaining_abs_levels(self, state, activeTrade):
"""
Returns price levels corresponding to remaing exit_levels for current trade
"""
cost_price, sl_price = self.get_trade_details(state)
cost_price, sl_price = self.get_trade_details(state, activeTrade)
if cost_price is None or sl_price is None:
return []
curr_sl_distance = np.abs(cost_price - sl_price)
@ -109,11 +109,11 @@ class SLOptimizer:
else:
return [cost_price - exit_level * curr_sl_distance for exit_level in self.exit_levels]
def eval_position(self, state, data) -> Tuple[float, float]:
def eval_position(self, state, data, activeTrade) -> Tuple[float, float]:
"""Evaluates optimalization for current position and returns if the given level was
met and how to adjust exit position.
"""
cost_price, sl_price = self.get_trade_details(state)
cost_price, sl_price = self.get_trade_details(state, activeTrade)
if cost_price is None or sl_price is None:
#print("no settings found")
return (None, None)

View File

@ -1,13 +1,12 @@
from v2realbot.strategy.base import StrategyState
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
from v2realbot.utils.utils import gaka, isrising, isfalling,zoneNY, price2dec, print, safe_get
#from icecream import install, ic
from rich import print as printanyway
from threading import Event
import os
from traceback import format_exc
from v2realbot.strategyblocks.activetrade.helpers import get_override_for_active_trade, normalize_tick, insert_SL_history
from v2realbot.strategyblocks.activetrade.helpers import get_signal_section_directive, normalize_tick, insert_SL_history
#pokud se cena posouva nasim smerem olespon o (0.05) nad (SL + 0.09val), posuneme SL o offset
#+ varianta - skoncit breakeven
@ -25,68 +24,75 @@ from v2realbot.strategyblocks.activetrade.helpers import get_override_for_active
# SL_trailing_stop_at_breakeven_short = true
# SL_trailing_stop_at_breakeven_long = true
def trail_SL_management(state: StrategyState, data):
if int(state.positions) != 0 and float(state.avgp)>0 and state.vars.pending is None:
def trail_SL_management(state: StrategyState, accountsWithActiveTrade, data):
#iterate over accountsWithActiveTrade
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(state.positions) < 0:
direction = TradeDirection.SHORT
smer = "short"
else:
direction = TradeDirection.LONG
smer = "long"
# zatim nastaveni SL plati pro vsechny - do budoucna per signal - pridat sekci
if int(positions) < 0:
direction = TradeDirection.SHORT
smer = "short"
else:
direction = TradeDirection.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_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)
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
#Aktivace SL pokud vystoupa na "offset", a nasledne posunuti o "step"
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))
offset_normalized = normalize_tick(state, data, offset) #to ticks and from options
step_normalized = normalize_tick(state, data, step)
def_SL_normalized = normalize_tick(state, data, def_SL)
if direction == TradeDirection.LONG:
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(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']:
state.vars.activeTrade.stoploss_value += step_normalized
insert_SL_history(state)
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)
elif direction == TradeDirection.SHORT:
move_SL_threshold = state.vars.activeTrade.stoploss_value - offset_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']:
state.vars.activeTrade.stoploss_value -= step_normalized
insert_SL_history(state)
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)
#SL_trailing_protection_window_short
directive_name = 'SL_trailing_protection_window_'+str(smer)
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))
index_to_compare = int(last_entry_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_entry_index=last_entry_index)
return
if sl_trailing_enabled is True:
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
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)):
state.ilog(lvl=1,e=f"SL trail STOP at breakeven {str(smer)} SL:{activeTrade.stoploss_value} UNCHANGED", stop_breakeven=stop_breakeven)
return
#Aktivace SL pokud vystoupa na "offset", a nasledne posunuti o "step"
offset_normalized = normalize_tick(state, data, offset) #to ticks and from options
step_normalized = normalize_tick(state, data, step)
def_SL_normalized = normalize_tick(state, data, def_SL)
if direction == TradeDirection.LONG:
move_SL_threshold = 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)
if (move_SL_threshold) < data['close']:
activeTrade.stoploss_value += step_normalized
insert_SL_history(state, activeTrade)
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)
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)

View File

@ -55,7 +55,11 @@ def populate_all_indicators(data, state: StrategyState):
#TODO tento lof patri spis do nextu classic SL - je poplatny typu stratefie
#TODO na toto se podivam, nejak moc zajasonovani a zpatky -
#PERF PROBLEM
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))
positions = state.account_variables[state.account].positions
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
if conf_bar == 1:

View File

@ -46,6 +46,10 @@ def populate_dynamic_slopeLP_indicator(data, state: StrategyState, name):
#typ leveho bodu [lastbuy - cena posledniho nakupu, baropen - cena otevreni baru]
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
if lookback_offset % 2 != 0:
lookback_offset += 1
@ -65,8 +69,8 @@ def populate_dynamic_slopeLP_indicator(data, state: StrategyState, name):
#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
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_buy_index))
if avgp > 0 and state.bars.index[-1] < int(state.vars.last_entry_index)+back_to_standard_after:
lb_index = -1 - (state.bars.index[-1] - int(state.vars.last_entry_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=}")
else:

View File

@ -1,7 +1,7 @@
from v2realbot.strategy.base import StrategyState
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
from v2realbot.common.model 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.directive_utils import get_conditions_from_configuration
from v2realbot.common.model import SLHistory

View File

@ -1,7 +1,7 @@
from v2realbot.strategy.base import StrategyState
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
from v2realbot.common.model 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.directive_utils import get_conditions_from_configuration
#import mlroom.utils.mlutils as ml

View File

@ -1,7 +1,7 @@
from v2realbot.strategy.base import StrategyState
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, Followup
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
from v2realbot.common.model 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.directive_utils import get_conditions_from_configuration
from v2realbot.common.model import SLHistory

View File

@ -1,6 +1,6 @@
from v2realbot.strategy.base import StrategyState
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus
from v2realbot.utils.utils import zoneNY, json_serial,transform_data
from v2realbot.common.model import TradeDirection, TradeStatus
from v2realbot.utils.utils import zoneNY, json_serial,transform_data, gaka
from datetime import datetime
#import random
import orjson
@ -10,97 +10,109 @@ from v2realbot.strategyblocks.indicators.helpers import value_or_indicator
#TODO nad prescribed trades postavit vstupni funkce
def execute_prescribed_trades(state: StrategyState, data):
##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 state.vars.activeTrade is not None or len(state.vars.prescribedTrades) == 0:
if len(state.vars.prescribedTrades) == 0 :
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)
#support multiaccount trades
state.ilog(lvl=1,e="evaluating prescr trades", trades=transform_data(state.vars.prescribedTrades, json_serial))
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']):
trade.status = TradeStatus.ACTIVATED
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.vars.activeTrade = trade
state.vars.last_buy_index = data["index"]
state.vars.last_in_index = data["index"]
break
execute_trade(state, data, trade) #TBD ERROR HANDLING
del accountsWithNoActiveTrade[trade.account.name] #to avoid other entries on the same account
continue
#evaluate shorts
if not state.vars.activeTrade:
for trade in state.vars.prescribedTrades:
if trade.status == TradeStatus.READY and trade.direction == TradeDirection.SHORT and (trade.entry_price is None or trade.entry_price <= data['close']):
state.ilog(lvl=1,e=f"evaluaed SHORT", trade=transform_data(trade, json_serial), prescrTrades=transform_data(state.vars.prescribedTrades, json_serial))
trade.status = TradeStatus.ACTIVATED
trade.last_update = datetime.fromtimestamp(state.time).astimezone(zoneNY)
state.vars.activeTrade = trade
state.vars.last_buy_index = data["index"]
state.vars.last_in_index = data["index"]
break
if trade.status == TradeStatus.READY and trade.direction == TradeDirection.SHORT and (trade.entry_price is None or trade.entry_price <= data['close']):
state.ilog(lvl=1,e=f"evaluaed SHORT", trade=transform_data(trade, json_serial), prescrTrades=transform_data(state.vars.prescribedTrades, json_serial))
trade.status = TradeStatus.ACTIVATED
trade.last_update = datetime.fromtimestamp(state.time).astimezone(zoneNY)
execute_trade(state, data, trade) #TBD ERROR HANDLING
del accountsWithNoActiveTrade[trade.account.name] #to avoid other entries on the same account
continue
#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}")
#defaultni goal price pripadne nastavujeme az v notifikaci
#TODO konzolidovat nize na spolecny kod pro short a long
#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á
#pokud neni nastaveno SL v prescribe, tak nastavuji default dle stratvars
if state.vars.activeTrade.stoploss_value is None:
sl_defvalue = get_default_sl_value(state, direction=state.vars.activeTrade.direction)
#TODO nastaveni SL az do notifikace, kdy je známá
#pokud neni nastaveno SL v prescribe, tak nastavuji default dle stratvars
if trade.stoploss_value is None:
sl_defvalue = get_default_sl_value(state=state, signal_name=trade.generated_by, direction=trade.direction)
if isinstance(sl_defvalue, (float, int)):
#normalizuji dle aktualni ceny
sl_defvalue_normalized = normalize_tick(state, data,sl_defvalue)
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.vars.activeTrade.stoploss_value }")
elif isinstance(sl_defvalue, str):
#from indicator
ind = sl_defvalue
sl_defvalue_abs = float(value_or_indicator(state, sl_defvalue))
if sl_defvalue_abs >= float(data['close']):
raise Exception(f"error in stoploss {ind} {sl_defvalue_abs} >= curr price")
state.vars.activeTrade.stoploss_value = sl_defvalue_abs
state.ilog(lvl=1,e=f"Nastaveno SL na {sl_defvalue_abs} dle indikatoru {ind}")
insert_SL_history(state)
state.vars.pending = state.vars.activeTrade.id
elif state.vars.activeTrade.direction == TradeDirection.SHORT:
state.ilog(lvl=1,e="odesilame SHORT 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.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
if isinstance(sl_defvalue, (float, int)):
#normalizuji dle aktualni ceny
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.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 }")
elif isinstance(sl_defvalue, str):
#from indicator
ind = sl_defvalue
sl_defvalue_abs = float(value_or_indicator(state, sl_defvalue))
if sl_defvalue_abs >= float(data['close']):
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.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)
elif trade.direction == TradeDirection.SHORT:
state.ilog(lvl=1,e="odesilame SHORT ORDER", trade=transform_data(trade, json_serial))
size = trade.size if trade.size is not None else state.vars.chunk
res = state.sell(size=size, account=trade.account)
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
#pokud neni nastaveno SL v prescribe, tak nastavuji default dle stratvars
if state.vars.activeTrade.stoploss_value is None:
sl_defvalue = get_default_sl_value(state, direction=state.vars.activeTrade.direction)
state.account_variables[trade.account.name].activeTrade = trade
#pokud neni nastaveno SL v prescribe, tak nastavuji default dle stratvars
if trade.stoploss_value is None:
sl_defvalue = get_default_sl_value(state, signal_name=trade.generated_by,direction=trade.direction)
if isinstance(sl_defvalue, (float, int)):
#normalizuji dle aktualni ceny
sl_defvalue_normalized = normalize_tick(state, data,sl_defvalue)
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.vars.activeTrade.stoploss_value }")
elif isinstance(sl_defvalue, str):
#from indicator
ind = sl_defvalue
sl_defvalue_abs = float(value_or_indicator(state, sl_defvalue))
if sl_defvalue_abs <= float(data['close']):
raise Exception(f"error in stoploss {ind} {sl_defvalue_abs} <= curr price")
state.vars.activeTrade.stoploss_value = sl_defvalue_abs
state.ilog(lvl=1,e=f"Nastaveno SL na {sl_defvalue_abs} dle indikatoru {ind}")
insert_SL_history(state)
state.vars.pending = state.vars.activeTrade.id
else:
state.ilog(lvl=1,e="unknow direction")
state.vars.activeTrade = None
if isinstance(sl_defvalue, (float, int)):
#normalizuji dle aktualni ceny
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.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 }")
elif isinstance(sl_defvalue, str):
#from indicator
ind = sl_defvalue
sl_defvalue_abs = float(value_or_indicator(state, sl_defvalue))
if sl_defvalue_abs <= float(data['close']):
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.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)
state.account_variables[trade.account.name].pending = trade.id
state.account_variables[trade.account.name].activeTrade = trade
state.account_variables[trade.account.name].last_entry_index =data["index"] #last_entry_index per account
state.vars.last_entry_index = data["index"] #spolecne pro vsechny accounty

View File

@ -1,6 +1,6 @@
from v2realbot.strategy.base import StrategyState
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, print, safe_get, gaka
from v2realbot.config import KW
from uuid import uuid4
from datetime import datetime
@ -31,6 +31,12 @@ def signal_search(state: StrategyState, data):
# slope10.out_short_if_above = 0
# 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():
execute_signal_generator(state, data, signalname)
@ -38,14 +44,21 @@ def signal_search(state: StrategyState, data):
# pokud je s cenou ceka se na cenu, pokud immmediate tak se hned provede
# to vse za predpokladu, ze neni aktivni trade
def execute_signal_generator(state, data, name):
def execute_signal_generator(state: StrategyState, 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] )
options = safe_get(state.vars.signals, name, None)
#add account from stratvars (if there) or default to self.state.account
if options is None:
state.ilog(lvl=1,e=f"No options for {name} in stratvars")
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:
return
@ -71,9 +84,12 @@ def execute_signal_generator(state, data, name):
state.ilog(lvl=1,e=f"{name} SHORT DISABLED")
if long_enabled is False:
state.ilog(lvl=1,e=f"{name} LONG DISABLED")
if long_enabled and go_conditions_met(state, data,signalname=name, direction=TradeDirection.LONG):
trade_made = None
#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)
state.vars.prescribedTrades.append(Trade(
account = account_long,
id=uuid4(),
last_update=datetime.fromtimestamp(state.time).astimezone(zoneNY),
status=TradeStatus.READY,
@ -83,9 +99,12 @@ def execute_signal_generator(state, data, name):
direction=TradeDirection.LONG,
entry_price=None,
stoploss_value = None))
elif short_enabled and go_conditions_met(state, data, signalname=name, direction=TradeDirection.SHORT):
trade_made = account_long
#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)
state.vars.prescribedTrades.append(Trade(
account=account_short,
id=uuid4(),
last_update=datetime.fromtimestamp(state.time).astimezone(zoneNY),
status=TradeStatus.READY,
@ -95,5 +114,5 @@ def execute_signal_generator(state, data, name):
direction=TradeDirection.SHORT,
entry_price=None,
stoploss_value = None))
else:
state.ilog(lvl=0,e=f"{name} NO SIGNAL")
return
state.ilog(lvl=0,e=f"{name} NO SIGNAL")

View File

@ -1,5 +1,5 @@
from v2realbot.strategy.base import StrategyState
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus
from v2realbot.common.model import Trade, TradeDirection, TradeStatus
import v2realbot.utils.utils as utls
from v2realbot.config import KW
from uuid import uuid4
@ -99,7 +99,8 @@ def get_multiplier(state: StrategyState, data, signaloptions: dict, direction: T
if probe_enabled:
#zatim pouze probe number 1 natvrdo, tzn. nesmi byt trade pro aktivace
if state.vars.last_in_index is None:
#zatim funguje pouze pro primarni
if state.account_variables[state.account].last_entry_index is None:
#probe_number = utls.safe_get(options, "probe_number",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)

View File

@ -12,7 +12,7 @@ from enum import Enum
import numpy as np
import v2realbot.controller.services as cs
#from rich import print
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get
from pathlib import Path
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY

View File

@ -11,7 +11,7 @@ import numpy as np
import v2realbot.controller.services as cs
from rich import print as richprint
from v2realbot.common.model import AnalyzerInputs
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.utils.utils import isrising, isfalling,zoneNY, price2dec, safe_get#, print
from pathlib import Path
from v2realbot.config import WEB_API_KEY, DATA_DIR, MEDIA_DIRECTORY
@ -23,7 +23,7 @@ from collections import defaultdict
from scipy.stats import zscore
from io import BytesIO
from typing import Tuple, Optional, List
from v2realbot.common.PrescribedTradeModel import TradeDirection, TradeStatus, Trade, TradeStoplossType
from v2realbot.common.model import TradeDirection, TradeStatus, Trade, TradeStoplossType
from collections import Counter
import vectorbtpro as vbt

View File

@ -9,8 +9,8 @@ import decimal
from v2realbot.enums.enums import RecordType, Mode, StartBarAlign
import pickle
import os
from v2realbot.common.model import StrategyInstance, Runner, RunArchive, RunArchiveDetail, Intervals, SLHistory, InstantIndicator
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
from v2realbot.common.model import StrategyInstance, Runner, RunArchive, RunArchiveDetail, Intervals, SLHistory, InstantIndicator, AccountVariables
from v2realbot.common.model import Trade, TradeDirection, TradeStatus, TradeStoplossType
from typing import List
import tomli
from v2realbot.config import DATA_DIR, ACCOUNT1_PAPER_API_KEY, ACCOUNT1_PAPER_SECRET_KEY
@ -36,6 +36,220 @@ import shutil
from filelock import FileLock
import v2realbot.utils.config_handler as cfh
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):
"""
@ -675,6 +889,7 @@ def json_serial(obj):
SLHistory: lambda obj: obj.__dict__,
InstantIndicator: lambda obj: obj.__dict__,
StrategyInstance: lambda obj: obj.__dict__,
AccountVariables: lambda obj: obj.__dict__
}
serializer = type_map.get(type(obj))

255
venv1_packages.txt Normal file
View File

@ -0,0 +1,255 @@
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