493 lines
28 KiB
Python
493 lines
28 KiB
Python
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, transform_data
|
|
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 alpaca.trading.enums import TradeEvent, OrderStatus
|
|
from v2realbot.indicators.indicators import ema
|
|
import orjson
|
|
from datetime import datetime
|
|
from rich import print as printanyway
|
|
from random import randrange
|
|
from alpaca.common.exceptions import APIError
|
|
import numpy as np
|
|
from threading import Event
|
|
from uuid import UUID, uuid4
|
|
from v2realbot.strategyblocks.indicators.indicators_hub import populate_all_indicators
|
|
from v2realbot.strategyblocks.activetrade.helpers import get_profit_target_price
|
|
|
|
class StrategyClassicSL(Strategy):
|
|
"""
|
|
Base override file for Classic Stop-Loss startegy
|
|
"""
|
|
def __init__(self, name: str, symbol: str, next: callable, init: callable, account: Account, mode: Mode = Mode.PAPER, stratvars: AttributeDict = None, open_rush: int = 30, close_rush: int = 30, pe: Event = None, se: Event = None, runner_id: UUID = None, ilog_save: bool = False) -> None:
|
|
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
|
|
|
|
##TODO zestručnit a dát pryč opakovací kód
|
|
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)
|
|
|
|
max_sum_profit_to_quit_rel = safe_get(self.state.vars, "max_sum_profit_to_quit_rel", None)
|
|
max_sum_loss_to_quit_rel = safe_get(self.state.vars, "max_sum_loss_to_quit_rel", None)
|
|
#load typ direktivy hard/soft cutoff
|
|
hard_cutoff = safe_get(self.state.vars, "hard_cutoff", False)
|
|
|
|
rel_profit = round(float(np.sum(self.state.rel_profit_cum)),5)
|
|
if max_sum_profit_to_quit_rel is not None:
|
|
if rel_profit >= float(max_sum_profit_to_quit_rel):
|
|
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"
|
|
if self.mode not in [Mode.BT, Mode.PREP]:
|
|
send_to_telegram(msg)
|
|
if hard_cutoff:
|
|
self.hard_stop = True
|
|
else:
|
|
self.soft_stop = True
|
|
return True
|
|
if max_sum_loss_to_quit_rel is not None:
|
|
if rel_profit < 0 and rel_profit <= float(max_sum_loss_to_quit_rel):
|
|
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"
|
|
if self.mode not in [Mode.BT, Mode.PREP]:
|
|
send_to_telegram(msg)
|
|
if hard_cutoff:
|
|
self.hard_stop = True
|
|
else:
|
|
self.soft_stop = True
|
|
return True
|
|
|
|
if max_sum_profit_to_quit is not None:
|
|
if float(self.state.profit) >= float(max_sum_profit_to_quit):
|
|
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"
|
|
if self.mode not in [Mode.BT, Mode.PREP]:
|
|
send_to_telegram(msg)
|
|
if hard_cutoff:
|
|
self.hard_stop = True
|
|
else:
|
|
self.soft_stop = True
|
|
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):
|
|
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"
|
|
if self.mode not in [Mode.BT, Mode.PREP]:
|
|
send_to_telegram(msg)
|
|
if hard_cutoff:
|
|
self.hard_stop = True
|
|
else:
|
|
self.soft_stop = True
|
|
return True
|
|
|
|
return False
|
|
|
|
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),
|
|
status=TradeStatus.READY,
|
|
size=size,
|
|
generated_by=signal_name,
|
|
direction=direction,
|
|
entry_price=None,
|
|
stoploss_value = None)
|
|
|
|
self.state.vars.prescribedTrades.append(trade_to_add)
|
|
|
|
self.state.vars.requested_followup = None
|
|
|
|
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
|
|
signal_name = None
|
|
##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))
|
|
|
|
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 data.event == TradeEvent.PARTIAL_FILL and self.state.wait_for_fill is None:
|
|
#timto si oznacime, ze po partialu s vlivem na PROFIT musime cekat na FILL a zaroven ukladame prum cenu, kterou potrebujeme na vypocet profitu u fillu
|
|
self.state.wait_for_fill = float(self.state.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)
|
|
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.")
|
|
avg_costs = bought_amount
|
|
|
|
trade_profit = round((avg_costs-bought_amount),2)
|
|
self.state.profit += trade_profit
|
|
|
|
rel_profit = 0
|
|
#spoctene celkovy relativni profit za trade v procentech ((trade_profit/vstup_naklady)*100)
|
|
if vstup_cena != 0 and int(data.order.qty) != 0:
|
|
rel_profit = round((trade_profit / (vstup_cena * float(data.order.qty))) * 100,5)
|
|
|
|
#pokud jde o finalni FILL - pridame do pole tento celkovy relativnich profit (ze ktereho se pocita kumulativni relativni profit)
|
|
rel_profit_cum_calculated = 0
|
|
partial_exit = False
|
|
partial_last = False
|
|
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)
|
|
partial_exit = True
|
|
else:
|
|
#jde o posledni z PARTIAL EXITU tzn.data.position_qty == 0
|
|
if len(self.state.docasny_rel_profit) > 0:
|
|
#pricteme aktualni rel profit
|
|
self.state.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 = []
|
|
partial_last = True
|
|
|
|
self.state.rel_profit_cum.append(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"BUY notif - SHORT PROFIT: {partial_exit=} {partial_last=} {round(float(trade_profit),3)} celkem:{round(float(self.state.profit),3)} rel:{float(rel_profit)} rel_cum:{round(rel_profit_cum_calculated,7)}", msg=str(data.event), rel_profit_cum=str(self.state.rel_profit_cum), bought_amount=bought_amount, avg_costs=avg_costs, trade_qty=data.qty, trade_price=data.price, orderid=str(data.order.id))
|
|
|
|
#zapsat profit do prescr.trades
|
|
for trade in self.state.vars.prescribedTrades:
|
|
if trade.id == self.state.vars.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)
|
|
trade_profit = trade.profit
|
|
trade.profit_sum = self.state.profit
|
|
trade.rel_profit = rel_profit
|
|
trade.rel_profit_cum = rel_profit_cum_calculated
|
|
signal_name = trade.generated_by
|
|
|
|
#Pokud FILL uzaviral celou pozici - uzavreme prescribed trade
|
|
if data.event == TradeEvent.FILL and data.position_qty == 0:
|
|
trade.status = TradeStatus.CLOSED
|
|
trade.exit_time = datetime.fromtimestamp(self.state.time).astimezone(zoneNY)
|
|
break
|
|
|
|
if data.event == TradeEvent.FILL:
|
|
#mazeme self.state.
|
|
self.state.wait_for_fill = None
|
|
#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)
|
|
setattr(tradeData, "prescribed_trade_id", self.state.vars.pending)
|
|
#self.state.ilog(f"updatnut tradeList o profit", tradeData=orjson.loads(orjson.dumps(tradeData, default=json_serial, option=orjson.OPT_PASSTHROUGH_DATETIME)))
|
|
setattr(tradeData, "rel_profit", rel_profit)
|
|
setattr(tradeData, "rel_profit_cum", rel_profit_cum_calculated)
|
|
|
|
#test na maximalni profit/loss, pokud vypiname pak uz nedelame pripdany reverzal
|
|
#kontrolu na max loss provadime az u FILLu, kdy je znama celkova castka
|
|
if data.event == TradeEvent.FILL and 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:
|
|
if trade.id == self.state.vars.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:
|
|
trade.entry_time = datetime.fromtimestamp(self.state.time).astimezone(zoneNY)
|
|
|
|
#zapsat do tradeList
|
|
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)
|
|
|
|
self.state.ilog(e="BUY: Jde o LONG nakuú nepocitame profit zatim")
|
|
|
|
if data.event == TradeEvent.FILL:
|
|
#zapisujeme last entry price
|
|
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:
|
|
dat = dict(close=data.price)
|
|
self.state.vars.activeTrade.goal_price = get_profit_target_price(self.state, dat, 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()
|
|
|
|
if o.status == OrderStatus.FILLED or o.status == OrderStatus.CANCELED:
|
|
#davame pryc pending
|
|
self.state.vars.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))
|
|
|
|
#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 data.event == TradeEvent.PARTIAL_FILL and self.state.wait_for_fill is None:
|
|
#timto si oznacime, ze po partialu s vlivem na PROFIT musime cekat na FILL a zaroven ukladame prum cenu, kterou potrebujeme na vypocet profitu u fillu
|
|
self.state.wait_for_fill = float(self.state.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)
|
|
else:
|
|
vstup_cena = 0
|
|
|
|
#podle prumerne ceny, kolik stalo toto mnozstvi
|
|
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.")
|
|
avg_costs = sold_amount
|
|
|
|
trade_profit = round((sold_amount - avg_costs),2)
|
|
self.state.profit += trade_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:
|
|
rel_profit = round((trade_profit / (vstup_cena * float(data.order.qty))) * 100,5)
|
|
|
|
rel_profit_cum_calculated = 0
|
|
partial_exit = False
|
|
partial_last = False
|
|
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)
|
|
partial_exit = True
|
|
else:
|
|
#jde o posledni z PARTIAL EXITU tzn.data.position_qty == 0
|
|
if len(self.state.docasny_rel_profit) > 0:
|
|
#pricteme aktualni rel profit
|
|
self.state.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 = []
|
|
partial_last = True
|
|
|
|
self.state.rel_profit_cum.append(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))
|
|
|
|
#zapsat profit do prescr.trades
|
|
for trade in self.state.vars.prescribedTrades:
|
|
if trade.id == self.state.vars.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)
|
|
trade_profit = trade.profit
|
|
trade.profit_sum = self.state.profit
|
|
trade.rel_profit = rel_profit
|
|
trade.rel_profit_cum = rel_profit_cum_calculated
|
|
signal_name = trade.generated_by
|
|
#Pokud FILL uzaviral celou pozici - uzavreme prescribed trade
|
|
if data.event == TradeEvent.FILL and data.position_qty == 0:
|
|
trade.status = TradeStatus.CLOSED
|
|
trade.exit_time = datetime.fromtimestamp(self.state.time).astimezone(zoneNY)
|
|
break
|
|
|
|
if data.event == TradeEvent.FILL:
|
|
#mazeme self.state.
|
|
self.state.wait_for_fill = None
|
|
#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)
|
|
setattr(tradeData, "prescribed_trade_id", self.state.vars.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)
|
|
#sem nejspis update skutecne vstupni ceny (celk.mnozstvi(order.qty) a avg_costs), to same i druhy smer
|
|
|
|
#kontrolu na max loss provadime az u FILLu, kdy je znama celkova castka
|
|
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:
|
|
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
|
|
for trade in self.state.vars.prescribedTrades:
|
|
if trade.id == self.state.vars.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:
|
|
trade.entry_time = datetime.fromtimestamp(self.state.time).astimezone(zoneNY)
|
|
|
|
#zapsat update profitu do tradeList
|
|
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)
|
|
|
|
self.state.ilog(e="SELL: Jde o SHORT nepocitame profit zatim")
|
|
|
|
if data.event == TradeEvent.FILL:
|
|
#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:
|
|
dat = dict(close=data.price)
|
|
self.state.vars.activeTrade.goal_price = get_profit_target_price(self.state, dat, 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)
|
|
if int(data.position_qty) == 0:
|
|
self.state.avgp = 0
|
|
|
|
self.state.ilog(e="SELL notifikace "+str(data.order.status), msg="update pozic", old_avgp=old_avgp, old_pos=old_pos, avgp=self.state.avgp, pos=self.state.positions, orderid=str(data.order.id))
|
|
#self.state.avgp, self.state.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()
|
|
#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
|
|
def strat_init(self):
|
|
#ic("strat INI function")
|
|
#lets connect method overrides
|
|
self.state.buy = self.buy
|
|
self.state.sell = self.sell
|
|
|
|
self.init(self.state)
|
|
|
|
def call_next(self, item):
|
|
#MAIN INDICATORS
|
|
populate_all_indicators(item, self.state)
|
|
|
|
#pro přípravu dat next nevoláme
|
|
if self.mode == Mode.PREP or self.soft_stop:
|
|
return
|
|
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):
|
|
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)
|
|
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)
|
|
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)
|
|
|
|
#overidden methods
|
|
# pouziva se pri vstupu short nebo exitu long
|
|
def sell(self, size = None, repeat: bool = False):
|
|
print("overriden sell method")
|
|
if size is None:
|
|
size = abs(int(self.state.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)
|
|
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)
|
|
printanyway("short - Maxim mnozstvi naplneno")
|
|
return 0
|
|
|
|
#self.state.blocksell = 1
|
|
#self.state.vars.lastbuyindex = self.state.bars['index'][-1]
|
|
#self.state.ilog(e="send MARKET SELL to if", msg="S:"+str(size), ltp=self.state.interface.get_last_price(self.state.symbol))
|
|
self.state.ilog(e="send MARKET SELL to if", msg="S:"+str(size), ltp=self.state.bars['close'][-1])
|
|
return self.state.interface.sell(size=size) |