exitadd direktiva + bugfixy na partialfill profit
This commit is contained in:
@ -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í
|
||||
|
||||
Binary file not shown.
@ -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"
|
||||
|
||||
|
||||
Binary file not shown.
@ -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:
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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'}">
|
||||
|
||||
@ -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
|
||||
|
||||
Reference in New Issue
Block a user