Files
v2realbot/v2realbot/ENTRY_ClassicSL_v01.py
2024-03-14 14:16:01 +01:00

303 lines
13 KiB
Python

import os,sys
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from v2realbot.strategy.base import StrategyState
from v2realbot.strategy.StrategyOrderLimitVykladaciNormalizedMYSELL import StrategyOrderLimitVykladaciNormalizedMYSELL
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account
from v2realbot.utils.utils import zoneNY, print, fetch_calendar_data, send_to_telegram
from v2realbot.utils.historicals import get_historical_bars
from datetime import datetime, timedelta
from rich import print as printanyway
from threading import Event
import os
from traceback import format_exc
from alpaca.data.timeframe import TimeFrame, TimeFrameUnit
from v2realbot.strategyblocks.newtrade.prescribedtrades import execute_prescribed_trades
from v2realbot.strategyblocks.newtrade.signals import signal_search
from v2realbot.strategyblocks.activetrade.activetrade_hub import manage_active_trade
from v2realbot.strategyblocks.inits.init_indicators import initialize_dynamic_indicators
from v2realbot.strategyblocks.inits.init_directives import intialize_directive_conditions
from v2realbot.strategyblocks.inits.init_attached_data import attach_previous_data
from alpaca.trading.client import TradingClient
from v2realbot.config import ACCOUNT1_PAPER_API_KEY, ACCOUNT1_PAPER_SECRET_KEY, DATA_DIR
from alpaca.trading.models import Calendar
from v2realbot.indicators.oscillators import rsi
from v2realbot.indicators.moving_averages import sma
import numpy as np
print(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
""""
Využívá: StrategyClassicSL
Klasická obousměrná multibuysignal strategie se stoplos.
Používá pouze market order, hlídá profit a stoploss.
Ve dvou fázích: 1) search and create prescriptions 2) evaluate prescriptions
list(prescribedTrade)
prescribedTrade:
- validfrom
- status .READY, ACTIVE, finished)
- direction (long/short)
- entry price:
- stoploss: (fixed, trailing)
Hlavní loop:
- indikátory
- if empty positions (avgp=0):
- no prescribed trades
- any prescribed trade?
- eval input
- eval eligible entries (do buy/sell)
- if positions (avgp <>0)
- eval exit (standard, forced by eod)
- if not exit - eval optimalizations
"""
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
#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)
def init(state: StrategyState):
#place to declare new vars
print("INIT v main",state.name)
#init klice v extData pro ulozeni historie SL
state.extData["sl_history"] = []
#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
#obsahuje aktivni Trade a jeho nastaveni
state.vars.activeTrade = None #pending/Trade
#obsahuje pripravene Trady ve frontě
state.vars.prescribedTrades = []
#flag pro reversal
state.vars.requested_followup = None
#TODO presunout inicializaci work_dict u podminek - sice hodnoty nepujdou zmenit, ale zlepsi se performance
#pripadne udelat refresh kazdych x-iterací
state.vars['sell_in_progress'] = False
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_update_time = 0
state.vars.reverse_position_waiting_amount = 0
#INIT promenne, ktere byly zbytecne ve stratvars
state.vars.pendingbuys={}
state.vars.limitka = None
state.vars.limitka_price=0
state.vars.jevylozeno=0
state.vars.blockbuy = 0
#models
state.vars.loaded_models = {}
#state attributes for martingale sizing mngmt
state.vars["transferables"] = {}
state.vars["transferables"]["martingale"] = dict(cont_loss_series_cnt=0)
#INITIALIZE CBAR INDICATORS - do vlastni funkce
#state.cbar_indicators['ivwap'] = []
state.vars.last_tick_price = 0
state.vars.last_tick_volume = 0
state.vars.last_tick_trades = 0
state.cbar_indicators['tick_price'] = []
state.cbar_indicators['tick_volume'] = []
state.cbar_indicators['tick_trades'] = []
state.cbar_indicators['CRSI'] = []
initialize_dynamic_indicators(state)
intialize_directive_conditions(state)
#attach part of yesterdays data, bars, indicators, cbar_indicators
attach_previous_data(state)
#intitialize indicator mapping (for use in operation) - mozna presunout do samostatne funkce prip dat do base kdyz se osvedci
local_dict_cbar_inds = {key: state.cbar_indicators[key] for key in state.cbar_indicators.keys() if key != "time"}
local_dict_inds = {key: state.indicators[key] for key in state.indicators.keys() if key != "time"}
local_dict_bars = {key: state.bars[key] for key in state.bars.keys() if key != "time"}
state.ind_mapping = {**local_dict_inds, **local_dict_bars, **local_dict_cbar_inds}
print("IND MAPPING DONE:", state.ind_mapping)
#30 DAYS historicall data fill - pridat do base pokud se osvedci
# -1 je vždy včerejšek v tomto případě
#diky tomu mají indikátory data 30 dní zpět (tzn. můžu počítat last day close, atp)
#do budoucna systematizovat přístup k historickým dat
# např. historicals.days state.historical.bars["days"]atp.
#nyní jednoucelne state.dailyBars
#LIVE a PAPER - bereme time now
#BT - bereme time bt_start
if state.mode in (Mode.LIVE, Mode.PAPER):
time_to = datetime.now(tz=zoneNY)
else:
time_to = state.bt.bp_from
#TBD NASLEDUJICI SEKCE BUDE PREDELANA, ABY UMOZNOVALA LIBOVOLNE ROZLISENI
#INDIKATORY SE BUDOU TAKE BRAT Z KONFIGURACE
#get 30 days (history_datetime_from musí být alespoň -2 aby to bralo i vcerejsek)
#history_datetime_from = time_to - timedelta(days=40)
#get previous market day
#time_to = time_to - timedelta(days=1)
#time_to = time_to.date()
#vypocet posledniho market dne - do samostatne funkce get_previous_market_day(today)
#time_to = time_to.date()
today = time_to.date()
several_days_ago = today - timedelta(days=60)
#printanyway(f"{today=}",f"{several_days_ago=}")
#clientTrading = TradingClient(ACCOUNT1_PAPER_API_KEY, ACCOUNT1_PAPER_SECRET_KEY, raw_data=False)
#get all market days from here to 40days ago
#calendar_request = GetCalendarRequest(start=several_days_ago,end=today)
cal_dates = fetch_calendar_data(several_days_ago, today)
#cal_dates = clientTrading.get_calendar(calendar_request)
#find the first market day - 40days ago
#history_datetime_from = zoneNY.localize(cal_dates[0].open)
history_datetime_from = cal_dates[0].open
#ulozime si dnesni market close
#pro automaticke ukonceni
#TODO pripadne enablovat na parametr
state.today_market_close = zoneNY.localize(cal_dates[-1].close)
# Find the previous market day
history_datetime_to = None
for session in reversed(cal_dates):
if session.date < today:
#history_datetime_to = zoneNY.localize(session.close)
history_datetime_to = session.close
break
#printanyway("Previous Market Day Close:", history_datetime_to)
#printanyway("Market day 40days ago Open:", history_datetime_from)
#printanyway(history_datetime_from, history_datetime_to)
#az do predchziho market dne dne
state.dailyBars = get_historical_bars(state.symbol, history_datetime_from, history_datetime_to, TimeFrame.Day)
#NOTE zatim pridano takto do baru dalsi indikatory
#BUDE PREDELANO - v rámci custom rozliseni a static indikátoru
if state.dailyBars is None:
print("Nepodařilo se načíst denní bary")
err_msg = f"Nepodařilo se načíst denní bary (get_historical_bars) pro {state.symbol} od {history_datetime_from} do {history_datetime_to} ve strat.init. Probably wrong symbol?"
send_to_telegram(err_msg)
raise Exception(err_msg)
#RSI vraci pouze pro vsechny + prepend with zeros nepocita prvnich N (dle rsi length)
rsi_calculated = rsi(state.dailyBars["vwap"], 14).tolist()
num_zeros_to_prepend = len(state.dailyBars["vwap"]) - len(rsi_calculated)
state.dailyBars["rsi"] = [0]*num_zeros_to_prepend + rsi_calculated
#VOLUME
volume_sma = sma(state.dailyBars["volume"], 10) #vraci celkovy pocet - 10
items_to_prepend = len(state.dailyBars["volume"]) - len(volume_sma)
volume_sma = np.hstack((np.full(items_to_prepend, np.nan), volume_sma))
#normalized divergence currvol-smavolume/currvol+smavolume
volume_data = np.array(state.dailyBars["volume"])
normalized_divergence = (volume_data - volume_sma) / (volume_data + volume_sma)
# Replace NaN values with 0 or some other placeholder if needed
normalized_divergence = np.nan_to_num(normalized_divergence)
volume_sma = np.nan_to_num(volume_sma)
state.dailyBars["volume_sma_divergence"] = normalized_divergence.tolist()
state.dailyBars["volume_sma"] = volume_sma.tolist()
#vwap_cum and divergence
volume_np = np.array(state.dailyBars["volume"])
close_np = np.array(state.dailyBars["close"])
high_np = np.array(state.dailyBars["high"])
low_np = np.array(state.dailyBars["low"])
vwap_cum_np = np.cumsum(((high_np + low_np + close_np) / 3) * volume_np) / np.cumsum(volume_np)
state.dailyBars["vwap_cum"] = vwap_cum_np.tolist()
normalized_divergence = (close_np - vwap_cum_np) / (close_np + vwap_cum_np)
#divergence close ceny a cumulativniho vwapu
state.dailyBars["div_vwap_cum"] = normalized_divergence.tolist()
#creates log returns for open, close, high and lows
open_np = np.array(state.dailyBars["open"])
state.dailyBars["open_log_return"] = np.log(open_np[1:] / open_np[:-1]).tolist()
state.dailyBars["close_log_return"] = np.log(close_np[1:] / close_np[:-1]).tolist()
state.dailyBars["high_log_return"] = np.log(high_np[1:] / high_np[:-1]).tolist()
state.dailyBars["low_log_return"] = np.log(low_np[1:] / low_np[:-1]).tolist()
#Features to emphasize the shape characteristics of each candlestick. For use in ML https://chat.openai.com/c/c1a22550-643b-4037-bace-3e810dbce087
# Calculate the ratios of
total_range = high_np - low_np
upper_shadow = (high_np - np.maximum(open_np, close_np)) / total_range
lower_shadow = (np.minimum(open_np, close_np) - low_np) / total_range
body_size = np.abs(close_np - open_np) / total_range
body_position = np.where(close_np >= open_np,
(close_np - low_np) / total_range,
(open_np - low_np) / total_range)
#other possibilities
# Open to Close Change: (close[-1] - open[-1]) / open[-1]
# High to Low Range: (high[-1] - low[-1]) / low[-1]
# Store the ratios in the bars dictionary
state.dailyBars['upper_shadow_ratio'] = upper_shadow.tolist()
state.dailyBars['lower_shadow_ratio'] = lower_shadow.tolist()
state.dailyBars['body_size_ratio'] = body_size.tolist()
state.dailyBars['body_position_ratio'] = body_position.tolist()
#printanyway("daily bars FILLED", state.dailyBars)
#zatim ukladame do extData - pro instant indicatory a gui
state.extData["dailyBars"] = state.dailyBars
def main():
name = os.path.basename(__file__)
se = Event()
pe = Event()
s = StrategyOrderLimitVykladaciNormalizedMYSELL(name = name, symbol = "BAC", account=Account.ACCOUNT1, next=next, init=init, stratvars=None, open_rush=10, close_rush=0, pe=pe, se=se, ilog_save=True)
s.set_mode(mode = Mode.BT,
debug = False,
start = datetime(2023, 4, 14, 10, 42, 0, 0, tzinfo=zoneNY),
end = datetime(2023, 4, 14, 14, 35, 0, 0, tzinfo=zoneNY),
cash=100000)
#na sekundovem baru nezaokrouhlovat MAcko
s.add_data(symbol="BAC",rectype=RecordType.BAR,resolution=2,minsize=100,update_ltp=True,align=StartBarAlign.ROUND,mintick=0, exthours=False)
#s.add_data(symbol="C",rectype=RecordType.BAR,timeframe=1,filters=None,update_ltp=True,align=StartBarAlign.ROUND,mintick=0)
s.start()
print("zastavujeme")
if __name__ == "__main__":
main()