exitadd direktiva + bugfixy na partialfill profit

This commit is contained in:
David Brazda
2023-09-13 09:56:51 +02:00
parent abf48e14df
commit e8f5805faf
8 changed files with 296 additions and 91 deletions

View File

@ -2,7 +2,7 @@ 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, OrderSide, OrderType
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, OrderSide, OrderType, Followup
from v2realbot.indicators.indicators import ema, natr, roc
from v2realbot.indicators.oscillators import rsi
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
@ -1011,8 +1011,9 @@ def next(data, state: StrategyState):
return price2dec(float(state.avgp)+normalized_max_profit,3) if int(state.positions) > 0 else price2dec(float(state.avgp)-normalized_max_profit,3)
def reverse_conditions_met(direction: TradeDirection):
#otestuje keyword podminky (napr. reverse_if, nebo exitadd_if)
def keyword_conditions_met(direction: TradeDirection, keyword: KW):
action = str(keyword).upper()
if direction == TradeDirection.LONG:
smer = "long"
else:
@ -1022,7 +1023,7 @@ def next(data, state: StrategyState):
exit_cond_only_on_confirmed = get_override_for_active_trade(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="REVERSAL CHECK COND ONLY ON CONFIRMED BAR")
state.ilog(lvl=0,e=f"{action} CHECK COND ONLY ON CONFIRMED BAR")
return False
#TOTO zatim u REVERSU neresime
@ -1066,37 +1067,123 @@ def next(data, state: StrategyState):
# return False
#bereme bud exit condition signalu, ktery activeTrade vygeneroval+ fallback na general
state.ilog(lvl=0,e=f"REVERSE CONDITIONS ENTRY {smer}", conditions=state.vars.conditions[KW.reverse])
state.ilog(lvl=0,e=f"{action} CONDITIONS ENTRY {smer}", conditions=state.vars.conditions[KW.reverse])
mother_signal = state.vars.activeTrade.generated_by
if mother_signal is not None:
cond_dict = state.vars.conditions[KW.reverse][state.vars.activeTrade.generated_by][smer]
cond_dict = state.vars.conditions[keyword][state.vars.activeTrade.generated_by][smer]
result, conditions_met = evaluate_directive_conditions(cond_dict, "OR")
state.ilog(lvl=1,e=f"REVERSE CONDITIONS of {mother_signal} =OR= {result}", **conditions_met, cond_dict=cond_dict)
state.ilog(lvl=1,e=f"{action} CONDITIONS of {mother_signal} =OR= {result}", **conditions_met, cond_dict=cond_dict)
if result:
return True
#OR neprosly testujeme AND
result, conditions_met = evaluate_directive_conditions(cond_dict, "AND")
state.ilog(lvl=1,e=f"REVERSE CONDITIONS of {mother_signal} =AND= {result}", **conditions_met, cond_dict=cond_dict)
state.ilog(lvl=1,e=f"{action} CONDITIONS of {mother_signal} =AND= {result}", **conditions_met, cond_dict=cond_dict)
if result:
return True
#pokud nemame mother signal nebo exit nevratil nic, fallback na common
cond_dict = state.vars.conditions[KW.reverse]["common"][smer]
cond_dict = state.vars.conditions[keyword]["common"][smer]
result, conditions_met = evaluate_directive_conditions(cond_dict, "OR")
state.ilog(lvl=1,e=f"REVERSE CONDITIONS of COMMON =OR= {result}", **conditions_met, cond_dict=cond_dict)
state.ilog(lvl=1,e=f"{action} CONDITIONS of COMMON =OR= {result}", **conditions_met, cond_dict=cond_dict)
if result:
return True
#OR neprosly testujeme AND
result, conditions_met = evaluate_directive_conditions(cond_dict, "AND")
state.ilog(lvl=0,e=f"REVERSE CONDITIONS of COMMON =AND= {result}", **conditions_met, cond_dict=cond_dict)
state.ilog(lvl=0,e=f"{action} CONDITIONS of COMMON =AND= {result}", **conditions_met, cond_dict=cond_dict)
if result:
return True
#DECOMISSIONOVANI - nahrazeno obacnou keyword_conditions_met
# def reverse_conditions_met(direction: TradeDirection):
# if direction == TradeDirection.LONG:
# smer = "long"
# else:
# smer = "short"
# directive_name = "exit_cond_only_on_confirmed"
# exit_cond_only_on_confirmed = get_override_for_active_trade(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="REVERSAL CHECK COND ONLY ON CONFIRMED BAR")
# return False
# #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))
# # #máme nastavený exit_cond_min_profit
# # # zjistíme, zda jsme v daném profit a případně nepustíme dál
# # # , zjistíme aktuální cenu a přičteme k avgp tento profit a podle toho pustime dal
# # if exit_cond_min_profit is not None:
# # exit_cond_min_profit_normalized = normalize_tick(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)
# # curr_price = float(data["close"])
# # state.ilog(lvl=0,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):
# # state.ilog(lvl=0,e=f"EXIT COND min profit PASS - POKRACUJEME")
# # else:
# # state.ilog(lvl=0,e=f"EXIT COND min profit NOT PASS")
# # return False
# #TOTO ZATIM NEMA VYZNAM
# # options = safe_get(state.vars, 'exit_conditions', None)
# # if options is None:
# # state.ilog(lvl=0,e="No options for exit conditions in stratvars")
# # return False
# # disable_exit_proteciton_when = dict(AND=dict(), OR=dict())
# # #preconditions
# # disable_exit_proteciton_when['disabled_in_config'] = safe_get(options, 'enabled', False) is False
# # #too good to be true (maximum profit)
# # #disable_sell_proteciton_when['tgtbt_reached'] = safe_get(options, 'tgtbt', False) is False
# # disable_exit_proteciton_when['disable_if_positions_above'] = int(safe_get(options, 'disable_if_positions_above', 0)) < abs(int(state.positions))
# # #testing preconditions
# # result, conditions_met = eval_cond_dict(disable_exit_proteciton_when)
# # if result:
# # state.ilog(lvl=0,e=f"EXIT_CONDITION for{smer} DISABLED by {conditions_met}", **conditions_met)
# # return False
# #bereme bud exit condition signalu, ktery activeTrade vygeneroval+ fallback na general
# state.ilog(lvl=0,e=f"REVERSE 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[KW.reverse][state.vars.activeTrade.generated_by][smer]
# result, conditions_met = evaluate_directive_conditions(cond_dict, "OR")
# state.ilog(lvl=1,e=f"REVERSE CONDITIONS of {mother_signal} =OR= {result}", **conditions_met, cond_dict=cond_dict)
# if result:
# return True
# #OR neprosly testujeme AND
# result, conditions_met = evaluate_directive_conditions(cond_dict, "AND")
# state.ilog(lvl=1,e=f"REVERSE CONDITIONS of {mother_signal} =AND= {result}", **conditions_met, cond_dict=cond_dict)
# if result:
# return True
# #pokud nemame mother signal nebo exit nevratil nic, fallback na common
# cond_dict = state.vars.conditions[KW.reverse]["common"][smer]
# result, conditions_met = evaluate_directive_conditions(cond_dict, "OR")
# state.ilog(lvl=1,e=f"REVERSE CONDITIONS of COMMON =OR= {result}", **conditions_met, cond_dict=cond_dict)
# if result:
# return True
# #OR neprosly testujeme AND
# result, conditions_met = evaluate_directive_conditions(cond_dict, "AND")
# state.ilog(lvl=0,e=f"REVERSE CONDITIONS of COMMON =AND= {result}", **conditions_met, cond_dict=cond_dict)
# if result:
# return True
def exit_conditions_met(direction: TradeDirection):
if direction == TradeDirection.LONG:
smer = "long"
@ -1276,9 +1363,9 @@ def next(data, state: StrategyState):
insert_SL_history()
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, def_SL_normalized=def_SL_normalized)
def close_position(direction: TradeDirection, reason: str, reverse: bool = False):
reversal_text = "REVERSAL" if reverse else ""
state.ilog(lvl=1,e=f"CLOSING TRADE {reversal_text} {reason} {str(direction)}", curr_price=data["close"], trade=state.vars.activeTrade)
def close_position(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)
if direction == TradeDirection.SHORT:
res = state.buy(size=abs(int(state.positions)))
if isinstance(res, int) and res < 0:
@ -1297,8 +1384,8 @@ def next(data, state: StrategyState):
state.vars.pending = state.vars.activeTrade.id
state.vars.activeTrade = None
state.vars.last_exit_index = data["index"]
if reverse:
state.vars.reverse_requested = True
if followup is not None:
state.vars.requested_followup = followup
def eval_close_position():
curr_price = float(data['close'])
@ -1324,21 +1411,33 @@ def next(data, state: StrategyState):
directive_name = 'reverse_for_SL_exit_short'
reverse_for_SL_exit = get_override_for_active_trade(directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
close_position(direction=TradeDirection.SHORT, reason="SL REACHED", reverse=reverse_for_SL_exit)
followup_action = Followup.REVERSE if reverse_for_SL_exit else None
close_position(direction=TradeDirection.SHORT, reason="SL REACHED", followup=followup_action)
return
#REVERSE BASED ON REVERSE CONDITIONS
if reverse_conditions_met(TradeDirection.SHORT):
close_position(direction=TradeDirection.SHORT, reason="REVERSE COND MET", reverse=True)
if keyword_conditions_met(direction=TradeDirection.SHORT, keyword=KW.reverse):
close_position(direction=TradeDirection.SHORT, reason="REVERSE COND MET", followup=Followup.REVERSE)
return
#EXIT ADD CONDITIONS MET (exit and add)
if keyword_conditions_met(direction=TradeDirection.SHORT, keyword=KW.exitadd):
close_position(direction=TradeDirection.SHORT, reason="EXITADD COND MET", followup=Followup.ADD)
return
#CLOSING BASED ON EXIT CONDITIONS
if exit_conditions_met(TradeDirection.SHORT):
directive_name = 'reverse_for_cond_exit_short'
reverse_for_cond_exit = get_override_for_active_trade(directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
close_position(direction=TradeDirection.SHORT, reason="EXIT COND MET", reverse=reverse_for_cond_exit)
reverse_for_cond_exit_short = get_override_for_active_trade(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(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(direction=TradeDirection.SHORT, reason="EXIT COND MET", followup=followup_action)
return
#PROFIT
@ -1359,22 +1458,34 @@ def next(data, state: StrategyState):
if curr_price < state.vars.activeTrade.stoploss_value:
directive_name = 'reverse_for_SL_exit_long'
reverse_for_SL_exit = get_override_for_active_trade(directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
close_position(direction=TradeDirection.LONG, reason="SL REACHED", reverse=reverse_for_SL_exit)
followup_action = Followup.REVERSE if reverse_for_SL_exit else None
close_position(direction=TradeDirection.LONG, reason="SL REACHED", followup=followup_action)
return
#REVERSE BASED ON REVERSE CONDITIONS
if reverse_conditions_met(TradeDirection.LONG):
close_position(direction=TradeDirection.LONG, reason="REVERSE COND MET", reverse=True)
if keyword_conditions_met(TradeDirection.LONG, KW.reverse):
close_position(direction=TradeDirection.LONG, reason="REVERSE COND MET", followup=Followup.REVERSE)
return
#EXIT ADD CONDITIONS MET (exit and add)
if keyword_conditions_met(TradeDirection.LONG, KW.exitadd):
close_position(direction=TradeDirection.LONG, reason="EXITADD COND MET", followup=Followup.ADD)
return
#EXIT CONDITIONS
if exit_conditions_met(TradeDirection.LONG):
directive_name = 'reverse_for_cond_exit_long'
reverse_for_cond_exit = get_override_for_active_trade(directive_name=directive_name, default_value=safe_get(state.vars, directive_name, False))
close_position(direction=TradeDirection.LONG, reason="EXIT CONDS MET", reverse=reverse_for_cond_exit)
reverse_for_cond_exit_long = get_override_for_active_trade(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(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(direction=TradeDirection.LONG, reason="EXIT CONDS MET", followup=followup_action)
return
#PROFIT
@ -1520,13 +1631,13 @@ def next(data, state: StrategyState):
#TESTUJEME GO SIGNAL
cond_dict = state.vars.conditions[KW.go][signalname][smer]
result, conditions_met = evaluate_directive_conditions(cond_dict, "OR")
state.ilog(lvl=0,e=f"EVAL GO SIGNAL {smer} =OR= {result}", **conditions_met, cond_dict=cond_dict)
state.ilog(lvl=1,e=f"EVAL GO SIGNAL {smer} =OR= {result}", **conditions_met, cond_dict=cond_dict)
if result:
return True
#OR neprosly testujeme AND
result, conditions_met = evaluate_directive_conditions(cond_dict, "AND")
state.ilog(lvl=0,e=f"EVAL GO SIGNAL {smer} =AND= {result}", **conditions_met, cond_dict=cond_dict)
state.ilog(lvl=1,e=f"EVAL GO SIGNAL {smer} =AND= {result}", **conditions_met, cond_dict=cond_dict)
if result:
return True
@ -1598,11 +1709,11 @@ def next(data, state: StrategyState):
state.ilog(lvl=1,e=f"PRECOND GENERAL not met {cond_met}", message=cond_met, precond_check=precond_check)
return False
state.ilog(lvl=0,e=f"{signalname} ALL PRECOND MET")
state.ilog(lvl=1,e=f"{signalname} ALL PRECOND MET")
return True
def execute_signal_generator(name):
state.ilog(lvl=1,e=f"SIGNAL SEARCH for {name}", cond_go=state.vars.conditions[KW.go][name], cond_dontgo=state.vars.conditions[KW.dont_go][name], cond_activate=state.vars.conditions[KW.activate][name] )
state.ilog(lvl=0,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)
if options is None:
@ -1814,7 +1925,7 @@ def init(state: StrategyState):
state.vars.conditions.setdefault(KW.go,{}).setdefault(signalname,{})[smer] = get_conditions_from_configuration(action=KW.go+"_" + smer +"_if", section=section)
state.vars.conditions.setdefault(KW.exit,{}).setdefault(signalname,{})[smer] = get_conditions_from_configuration(action=KW.exit+"_" + smer +"_if", section=section)
state.vars.conditions.setdefault(KW.reverse,{}).setdefault(signalname,{})[smer] = get_conditions_from_configuration(action=KW.reverse+"_" + smer +"_if", section=section)
state.vars.conditions.setdefault(KW.exitadd,{}).setdefault(signalname,{})[smer] = get_conditions_from_configuration(action=KW.exitadd+"_" + smer +"_if", section=section)
# state.vars.work_dict_dont_do[signalname+"_"+ smer] = get_work_dict_with_directive(starts_with=signalname+"_dont_"+ smer +"_if")
# state.vars.work_dict_signal_if[signalname+"_"+ smer] = get_work_dict_with_directive(starts_with=signalname+"_"+smer+"_if")
@ -1823,7 +1934,7 @@ def init(state: StrategyState):
for smer in TradeDirection:
state.vars.conditions.setdefault(KW.exit,{}).setdefault("common",{})[smer] = get_conditions_from_configuration(action=KW.exit+"_" + smer +"_if", section=section)
state.vars.conditions.setdefault(KW.reverse,{}).setdefault("common",{})[smer] = get_conditions_from_configuration(action=KW.reverse+"_" + smer +"_if", section=section)
state.vars.conditions.setdefault(KW.exitadd,{}).setdefault("common",{})[smer] = get_conditions_from_configuration(action=KW.exitadd+"_" + smer +"_if", section=section)
#init klice v extData pro ulozeni historie SL
state.extData["sl_history"] = []
@ -1835,7 +1946,7 @@ def init(state: StrategyState):
#obsahuje pripravene Trady ve frontě
state.vars.prescribedTrades = []
#flag pro reversal
state.vars.reverse_requested = False
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í

View File

@ -104,8 +104,13 @@ ACCOUNT2_PAPER_PAPER = True
ACCOUNT2_PAPER_FEED = DataFeed.IEX
class KW:
activate: str = "activate"
dont_go: str = "dont_go"
go: str = "go"
activate: str = "activate"
# wip addsize: str = "addsize"
exit: str = "exit"
reverse: str = "reverse"
#wip exitsize: str = "exitsize"
exitadd: str = "exitadd"
reverse: str = "reverse"
#exitaddsize: str = "exitaddsize"

View File

@ -12,6 +12,10 @@ class Order:
self.filled_time = filled_time
self.limit_price = limit_price
class Followup(str, Enum):
REVERSE = "reverse"
ADD = "add"
class FillCondition(str, Enum):
"""
Execution settings:

View File

@ -27,8 +27,10 @@ from queue import Queue, Empty
from threading import Thread
import asyncio
from v2realbot.common.db import insert_queue, insert_conn, pool
from v2realbot.utils.utils import json_serial
from v2realbot.utils.utils import json_serial, send_to_telegram
from uuid import uuid4
from sqlite3 import OperationalError
from time import sleep
#from async io import Queue, QueueEmpty
# install()
@ -519,17 +521,25 @@ def insert_queue2db():
# Retrieve data from the queue
data = insert_queue.get()
# Unpack the data
runner_id, loglist = data
c = insert_conn.cursor()
insert_data = []
for i in loglist:
row = (str(runner_id), i["time"], json.dumps(i, default=json_serial))
insert_data.append(row)
c.executemany("INSERT INTO runner_logs VALUES (?,?,?)", insert_data)
insert_conn.commit()
# Mark the task as done in the queue
try:
# Unpack the data
runner_id, loglist = data
c = insert_conn.cursor()
insert_data = []
for i in loglist:
row = (str(runner_id), i["time"], json.dumps(i, default=json_serial))
insert_data.append(row)
c.executemany("INSERT INTO runner_logs VALUES (?,?,?)", insert_data)
insert_conn.commit()
# Mark the task as done in the queue
except OperationalError as e:
send_to_telegram("insert logs daemon returned" + str(e) + "RETRYING")
if "database is locked" in str(e):
# Database is locked, wait for a while and retry
insert_queue.put(data) # Put the data back into the queue for retry
sleep(1) # You can adjust the sleep duration
else:
raise # If it's another error, raise it
#join cekej na dokonceni vsech
for i in cs.db.runners:

View File

@ -8,6 +8,7 @@ console.log("CHART_SHOW_TEXT archchart", CHART_SHOW_TEXT)
// var volumeSeries = null
var markersLine = null
var avgBuyLine = null
var profitLine = null
var slLine = []
//TRANSFORM object returned from REST API get_arch_run_detail
//to series and markers required by lightweigth chart
@ -111,6 +112,7 @@ function transform_data(data) {
//get markers - avgp line for all buys
var avgp_buy_line = []
var avgp_markers = []
var sum_profit_line = []
var markers = []
var markers_line = []
var last_timestamp = 0.1
@ -139,9 +141,9 @@ function transform_data(data) {
last_timestamp = timestamp
iterator = 0.002
}
//puvodne bylo pro buy
//pro sell muzeme teoreticky taky mit buyline (pri shortu)
if ((trade.order.side == "buy") || (trade.order.side == "sell")) {
//AVG BUY LINE - zatim docasne vypiname
if (((trade.order.side == "buy") || (trade.order.side == "sell")) && 1==2) {
//avgp lajnu vytvarime jen pokud je v tradeventu prumerna cena
if ((trade.pos_avg_price !== null) && (trade.pos_avg_price !== 0)) {
//line pro avgp markers
@ -161,6 +163,26 @@ function transform_data(data) {
}
}
if ((trade.order.side == "buy") || (trade.order.side == "sell")) {
//avgp lajnu vytvarime jen pokud je v tradeventu prumerna cena
if ((trade.profit_sum !== null)) {
//line pro avgp markers
obj["time"] = timestamp;
obj["value"] = trade.profit_sum.toFixed(1);
sum_profit_line.push(obj)
//avgp markers pro prumernou cenu aktualnich pozic
// a_markers["time"] = timestamp
// a_markers["position"] = "aboveBar"
// a_markers["color"] = "#e8c76d"
// a_markers["shape"] = "arrowDown"
// a_markers["text"] = trade.profit_sum.toFixed(1);
// //if (CHART_SHOW_TEXT)
// //a_markers["text"] = trade.position_qty + " " + parseFloat(trade.pos_avg_price).toFixed(3)
// //a_markers["text"] = CHART_SHOW_TEXT ? trade.position_qty + "/" + parseFloat(trade.pos_avg_price).toFixed(3) :trade.position_qty
// avgp_markers.push(a_markers)
}
}
//buy sell markery
@ -172,7 +194,7 @@ function transform_data(data) {
//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.qty % 1000 === 0) ? (trade.qty / 1000).toFixed(1) + 'K' : trade.qty
qt_optimized = (trade.order.qty % 1000 === 0) ? (trade.order.qty / 1000).toFixed(1) + 'K' : trade.order.qty
if (CHART_SHOW_TEXT) {
//včetně qty
@ -215,8 +237,10 @@ function transform_data(data) {
markers_line.sort(sorter)
avgp_buy_line.sort(sorter)
avgp_markers.sort(sorter)
sum_profit_line.sort(sorter)
transformed["avgp_buy_line"] = avgp_buy_line
transformed["avgp_buy_line"] = avgp_buy_line
transformed["sum_profit_line"] = sum_profit_line
transformed["avgp_markers"] = avgp_markers
transformed["markers"] = markers
transformed["markers_line"] = markers_line
@ -366,7 +390,15 @@ function chart_archived_run(archRecord, data, oneMinuteBars) {
}
container1.append(indbuttonElement);
display_buy_markers();
//TADY JSEM SKONCIL - toto nize predelat na hide button pro display bar markers
// btnElement = document.getElementById("pricelineButtons")
// var indbuttonElement = populate_indicator_buttons(false);
// if (btnElement) {
// container1.removeChild(btnElement);
// }
//container1.append(indbuttonElement);
if (last_range) {
chart.timeScale().setVisibleRange(last_range);
@ -604,6 +636,11 @@ function chart_archived_run(archRecord, data, oneMinuteBars) {
//displays (redraws) buy markers
function display_buy_markers() {
if (profitLine) {
chart.removeSeries(profitLine)
}
if (avgBuyLine) {
chart.removeSeries(avgBuyLine)
}
@ -650,10 +687,31 @@ function chart_archived_run(archRecord, data, oneMinuteBars) {
slLine.push(slLine_temp)
//xx
})
})
//}
if (transformed_data["sum_profit_line"].length > 0) {
profitLine = chart.addLineSeries({
// title: "avgpbuyline",
color: '#e8c76d',
// color: 'transparent',
lineWidth: 1,
lastValueVisible: false
});
profitLine.applyOptions({
lastValueVisible: false,
priceLineVisible: false,
priceScaleId: "own"
});
profitLine.setData(transformed_data["sum_profit_line"]);
//profitLine.setMarkers(transformed_data["sum_profit_line_markers"]);
}
if (transformed_data["avgp_buy_line"].length > 0) {
avgBuyLine = chart.addLineSeries({
// title: "avgpbuyline",
@ -714,6 +772,7 @@ function chart_archived_run(archRecord, data, oneMinuteBars) {
toolTip.innerHTML = "";
var data = param.seriesData.get(markersLine);
var data2 = param.seriesData.get(avgBuyLine);
var profitdata = param.seriesData.get(profitLine);
if ((data !== undefined) || (data2 !== undefined)) {
//param.seriesData.forEach((value, key) => {
//console.log("key",key)
@ -733,9 +792,11 @@ function chart_archived_run(archRecord, data, oneMinuteBars) {
buy_price = parseFloat(data2.value).toFixed(3)
}
toolTip.innerHTML += `<div>POS:${tradeDetails.get(param.time).position_qty}/${buy_price}</div><div>T:${tradeDetails.get(param.time).qty}/${data.value}</div>`;
//inspirace
toolTip.innerHTML += `<div>POS:${tradeDetails.get(param.time).position_qty}/${buy_price}</div><div>T:${tradeDetails.get(param.time).order.qty}/${data.value}</div>`;
if (profitdata !== undefined) {
toolTip.innerHTML += `<div>P:${parseFloat(profitdata.value).toFixed(1)}</div>`
}
//inspirace
// toolTip.innerHTML = `<div style="color: ${'#2962FF'}">Apple Inc.</div><div style="font-size: 24px; margin: 4px 0px; color: ${'black'}">
// ${Math.round(100 * price) / 100}
// </div><div style="color: ${'black'}">

View File

@ -1,7 +1,7 @@
from v2realbot.strategy.base import Strategy
from v2realbot.utils.utils import parse_alpaca_timestamp, ltp, AttributeDict,trunc,price2dec, zoneNY, print, json_serial, safe_get, get_tick, send_to_telegram
from v2realbot.utils.tlog import tlog, tlog_exception
from v2realbot.enums.enums import Mode, Order, Account, RecordType
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
@ -25,7 +25,7 @@ class StrategyClassicSL(Strategy):
super().__init__(name, symbol, next, init, account, mode, stratvars, open_rush, close_rush, pe, se, runner_id, ilog_save)
#zkontroluje zda aktualni profit/loss - nedosahnul limit a pokud ano tak vypne strategii
async def check_max_profit_loss(self):
async def stop_when_max_profit_loss(self):
self.state.ilog(e="CHECK MAX PROFIT")
max_sum_profit_to_quit = safe_get(self.state.vars, "max_sum_profit_to_quit", None)
max_sum_loss_to_quit = safe_get(self.state.vars, "max_sum_loss_to_quit", None)
@ -36,15 +36,19 @@ class StrategyClassicSL(Strategy):
self.state.vars.pending = "max_sum_profit_to_quit"
send_to_telegram(f"QUITTING MAX SUM PROFIT REACHED {max_sum_profit_to_quit=} {self.state.profit=}")
self.se.set()
return True
if max_sum_loss_to_quit is not None:
if float(self.state.profit) < 0 and float(self.state.profit) <= float(max_sum_loss_to_quit):
self.state.ilog(e=f"QUITTING MAX SUM LOSS REACHED {max_sum_loss_to_quit=} {self.state.profit=}")
self.state.vars.pending = "max_sum_loss_to_quit"
send_to_telegram(f"QUITTING MAX SUM LOSS REACHED {max_sum_loss_to_quit=} {self.state.profit=}")
self.se.set()
return True
return False
async def add_reversal(self, direction: TradeDirection, size: int, signal_name: str):
async def add_followup(self, direction: TradeDirection, size: int, signal_name: str):
trade_to_add = Trade(
id=uuid4(),
last_update=datetime.fromtimestamp(self.state.time).astimezone(zoneNY),
@ -57,9 +61,9 @@ class StrategyClassicSL(Strategy):
self.state.vars.prescribedTrades.append(trade_to_add)
self.state.vars.reverse_requested = None
self.state.vars.requested_followup = None
self.state.ilog(e=f"REVERZAL {direction} added to prescr.trades {signal_name=} {size=}", trade=trade_to_add)
self.state.ilog(e=f"FOLLOWUP {direction} added to prescr.trades {signal_name=} {size=}", trade=trade_to_add)
async def orderUpdateBuy(self, data: TradeUpdate):
o: Order = data.order
@ -92,23 +96,27 @@ class StrategyClassicSL(Strategy):
trade.profit_sum = self.state.profit
signal_name = trade.generated_by
if data.event == TradeEvent.FILL:
#zapsat update profitu do tradeList
for tradeData in self.state.tradeList:
if tradeData.execution_id == data.execution_id:
#pridat jako attribut, aby proslo i na LIVE a PAPPER, kde se bere TradeUpdate z Alpaca
setattr(tradeData, "profit", trade_profit)
setattr(tradeData, "profit_sum", self.state.profit)
setattr(tradeData, "signal_name", signal_name)
#self.state.ilog(f"updatnut tradeList o profit", tradeData=json.loads(json.dumps(tradeData, default=json_serial)))
for tradeData in self.state.tradeList:
if tradeData.execution_id == data.execution_id:
#pridat jako attribut, aby proslo i na LIVE a PAPPER, kde se bere TradeUpdate z Alpaca
setattr(tradeData, "profit", trade_profit)
setattr(tradeData, "profit_sum", self.state.profit)
setattr(tradeData, "signal_name", signal_name)
#self.state.ilog(f"updatnut tradeList o profit", tradeData=json.loads(json.dumps(tradeData, default=json_serial)))
#test na maximalni profit/loss
await self.check_max_profit_loss()
#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.reverse_requested:
await self.add_reversal(direction=TradeDirection.LONG, size=o.qty, signal_name=signal_name)
#test na maximalni profit/loss, pokud vypiname pak uz nedelame pripdany reverzal
if await self.stop_when_max_profit_loss() is False:
#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:
#zatim stejna SIZE
await self.add_followup(direction=TradeDirection.SHORT, size=o.qty, signal_name=signal_name)
else:
#zjistime nazev signalu a updatneme do tradeListu - abychom meli svazano
for trade in self.state.vars.prescribedTrades:
@ -161,20 +169,25 @@ class StrategyClassicSL(Strategy):
trade.profit_sum = self.state.profit
signal_name = trade.generated_by
#zapsat update profitu do tradeList
for tradeData in self.state.tradeList:
if tradeData.execution_id == data.execution_id:
#pridat jako attribut, aby proslo i na LIVE a PAPPER, kde se bere TradeUpdate z Alpaca
setattr(tradeData, "profit", trade_profit)
setattr(tradeData, "profit_sum", self.state.profit)
setattr(tradeData, "signal_name", signal_name)
#self.state.ilog(f"updatnut tradeList o profi {str(tradeData)}")
if data.event == TradeEvent.FILL:
#zapsat update profitu do tradeList
for tradeData in self.state.tradeList:
if tradeData.execution_id == data.execution_id:
#pridat jako attribut, aby proslo i na LIVE a PAPPER, kde se bere TradeUpdate z Alpaca
setattr(tradeData, "profit", trade_profit)
setattr(tradeData, "profit_sum", self.state.profit)
setattr(tradeData, "signal_name", signal_name)
#self.state.ilog(f"updatnut tradeList o profi {str(tradeData)}")
await self.check_max_profit_loss()
if 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.reverse_requested:
await self.add_reversal(direction=TradeDirection.SHORT, size=data.order.qty, signal_name=signal_name)
#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:
await self.add_followup(direction=TradeDirection.SHORT, size=data.order.qty, signal_name=signal_name)
elif self.state.vars.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
@ -206,6 +219,7 @@ class StrategyClassicSL(Strategy):
#pri chybe api nechavame puvodni hodnoty
if a != -1:
self.state.avgp, self.state.positions = a,p
else: self.state.ilog(e=f"Chyba pri dotažení self.interface.pos() {a}")
#ic(self.state.avgp, self.state.positions)
#this parent method is called by strategy just once before waiting for first data