pridan prace s profitem a jeho zobrazovani v gui

This commit is contained in:
David Brazda
2023-08-27 13:24:14 +02:00
parent baa3715f07
commit a94e2ed5e4
9 changed files with 89 additions and 29 deletions

View File

@ -8,6 +8,7 @@ from v2realbot.indicators.oscillators import rsi
from v2realbot.common.PrescribedTradeModel import Trade, TradeDirection, TradeStatus, TradeStoplossType
from v2realbot.utils.utils import ltp, isrising, isfalling,trunc,AttributeDict, zoneNY, price2dec, print, safe_get, get_tick, round2five, is_open_rush, is_close_rush, eval_cond_dict, Average, crossed_down, crossed_up, crossed, is_pivot, json_serial
from datetime import datetime
from uuid import uuid4
import json
#from icecream import install, ic
#from rich import print
@ -769,7 +770,7 @@ def next(data, state: StrategyState):
curr_price = float(data['close'])
state.ilog(e="Eval CLOSE", price=curr_price, pos=state.positions, avgp=state.avgp, pending=state.vars.pending, activeTrade=str(state.vars.activeTrade))
if int(state.positions) != 0 and float(state.avgp)>0 and state.vars.pending is False:
if int(state.positions) != 0 and float(state.avgp)>0 and state.vars.pending is None:
#pevny target - presunout toto do INIT a pak jen pristupovat
goal_price = get_profit_target_price()
max_price = get_max_profit_price()
@ -790,7 +791,7 @@ def next(data, state: StrategyState):
res = state.buy(size=abs(state.positions))
if isinstance(res, int) and res < 0:
raise Exception(f"error in required operation STOPLOSS BUY {res}")
state.vars.pending = True
state.vars.pending = state.vars.activeTrade.id
state.vars.activeTrade = None
return
@ -799,7 +800,7 @@ def next(data, state: StrategyState):
res = state.buy(size=abs(state.positions))
if isinstance(res, int) and res < 0:
raise Exception(f"error in required operation EXIT COND BUY {res}")
state.vars.pending = True
state.vars.pending = state.vars.activeTrade.id
state.vars.activeTrade = None
state.ilog(e=f"EXIT COND MET. market BUY was sent {curr_price=}", positions=state.positions, avgp=state.avgp)
return
@ -815,7 +816,7 @@ def next(data, state: StrategyState):
res = state.buy(size=abs(state.positions))
if isinstance(res, int) and res < 0:
raise Exception(f"error in required operation PROFIT BUY {res}")
state.vars.pending = True
state.vars.pending = state.vars.activeTrade.id
state.vars.activeTrade = None
state.ilog(e=f"PROFIT MET EXIT. market BUY was sent {curr_price=} {max_price_signal=}", positions=state.positions, avgp=state.avgp)
return
@ -832,7 +833,7 @@ def next(data, state: StrategyState):
res = state.sell(size=state.positions)
if isinstance(res, int) and res < 0:
raise Exception(f"error in required operation STOPLOSS SELL {res}")
state.vars.pending = True
state.vars.pending = state.vars.activeTrade.id
state.vars.activeTrade = None
return
@ -840,7 +841,7 @@ def next(data, state: StrategyState):
res = state.sell(size=abs(state.positions))
if isinstance(res, int) and res < 0:
raise Exception(f"error in required operation EXIT COND SELL {res}")
state.vars.pending = True
state.vars.pending = state.vars.activeTrade.id
state.vars.activeTrade = None
state.ilog(e=f"EXIT COND MET. market SELL was sent {curr_price=}", positions=state.positions, avgp=state.avgp)
return
@ -856,7 +857,7 @@ def next(data, state: StrategyState):
res = state.sell(size=state.positions)
if isinstance(res, int) and res < 0:
raise Exception(f"error in required operation PROFIT SELL {res}")
state.vars.pending = True
state.vars.pending = state.vars.activeTrade.id
state.vars.activeTrade = None
state.ilog(e=f"PROFIT MET EXIT. market SELL was sent {curr_price=} {max_price_signal=}", positions=state.positions, avgp=state.avgp, sellinprogress=state.vars.sell_in_progress)
return
@ -897,7 +898,7 @@ def next(data, state: StrategyState):
sl_defvalue_normalized = get_tick(data['close'],sl_defvalue)
state.vars.activeTrade.stoploss_value = float(data['close']) - sl_defvalue_normalized
state.ilog(e=f"Nastaveno SL na {sl_defvalue}, priced normalized: {sl_defvalue_normalized} price: {state.vars.activeTrade.stoploss_value }")
state.vars.pending = True
state.vars.pending = state.vars.activeTrade.id
elif state.vars.activeTrade.direction == TradeDirection.SHORT:
state.ilog(e="odesilame SHORT ORDER",trade=json.loads(json.dumps(state.vars.activeTrade, default=json_serial)))
res = state.sell(size=state.vars.chunk)
@ -910,7 +911,7 @@ def next(data, state: StrategyState):
sl_defvalue_normalized = get_tick(data['close'],sl_defvalue)
state.vars.activeTrade.stoploss_value = float(data['close']) + sl_defvalue_normalized
state.ilog(e=f"Nastaveno SL na {sl_defvalue}, priced normalized: {sl_defvalue_normalized} price: {state.vars.activeTrade.stoploss_value }")
state.vars.pending = True
state.vars.pending = state.vars.activeTrade.id
else:
state.ilog(e="unknow direction")
state.vars.activeTrade = None
@ -1035,13 +1036,17 @@ def next(data, state: StrategyState):
#common signals based on 1) configured signals in stratvars
#toto umoznuje jednoduchy prescribed trade bez ceny
if conditions_met(signalname=name, direction=TradeDirection.LONG):
state.vars.prescribedTrades.append(Trade(validfrom=datetime.now(tz=zoneNY),
state.vars.prescribedTrades.append(Trade(
id=uuid4(),
validfrom=datetime.now(tz=zoneNY),
status=TradeStatus.READY,
direction=TradeDirection.LONG,
entry_price=None,
stoploss_value = None))
elif conditions_met(signalname=name, direction=TradeDirection.SHORT):
state.vars.prescribedTrades.append(Trade(validfrom=datetime.now(tz=zoneNY),
state.vars.prescribedTrades.append(Trade(
id=uuid4(),
validfrom=datetime.now(tz=zoneNY),
status=TradeStatus.READY,
direction=TradeDirection.SHORT,
entry_price=None,
@ -1083,24 +1088,24 @@ def next(data, state: StrategyState):
#MAIN LOOP
lp = data['close']
state.ilog(e="ENTRY", msg=f"LP:{lp} P:{state.positions}/{round(float(state.avgp),3)} SL:{state.vars.activeTrade.stoploss_value} profit:{round(float(state.profit),2)} Trades:{len(state.tradeList)}", activeTrade=json.loads(json.dumps(state.vars.activeTrade, default=json_serial)), prescribedTrades=json.loads(json.dumps(state.vars.prescribedTrades, default=json_serial)), pending=str(state.vars.pending), last_price=lp, data=data, stratvars=str(state.vars))
state.ilog(e="ENTRY", msg=f"LP:{lp} P:{state.positions}/{round(float(state.avgp),3)} SL:{state.vars.activeTrade.stoploss_value if state.vars.activeTrade is not None else None} profit:{round(float(state.profit),2)} Trades:{len(state.tradeList)}", activeTrade=json.loads(json.dumps(state.vars.activeTrade, default=json_serial)), prescribedTrades=json.loads(json.dumps(state.vars.prescribedTrades, default=json_serial)), pending=str(state.vars.pending), last_price=lp, data=data, stratvars=str(state.vars))
inds = get_last_ind_vals()
state.ilog(e="Indikatory", **inds)
#TODO dat do initu inciializaci work directory pro directivy
#pokud mame prazdne pozice a neceka se na nic
if state.positions == 0 and state.vars.pending is False:
if state.positions == 0 and state.vars.pending is None:
execute_prescribed_trades()
#pokud se neaktivoval nejaky trade, poustime signal search - ale jen jednou za bar?
#if conf_bar == 1:
if state.vars.pending is False:
if state.vars.pending is None:
signal_search()
#pro jistotu ihned zpracujeme
execute_prescribed_trades()
#mame aktivni trade a neceka se nani
elif state.vars.activeTrade and state.vars.pending is False:
elif state.vars.activeTrade and state.vars.pending is None:
manage_active_trade() #optimalize, close
# - close means change status in prescribed Trends,update profit, delete from activeTrade
@ -1158,7 +1163,7 @@ def init(state: StrategyState):
#nove atributy na rizeni tradu
#identifikuje provedenou změnu na Tradu (neděláme změny dokud nepřijde potvrzeni z notifikace)
state.vars.pending = False
state.vars.pending = None
#obsahuje aktivni Trade a jeho nastaveni
state.vars.activeTrade = None #pending/Trade
#obsahuje pripravene Trady ve frontě

View File

@ -2,7 +2,7 @@ from enum import Enum
from datetime import datetime
from pydantic import BaseModel
from typing import Any, Optional, List, Union
from uuid import UUID
class TradeStatus(str, Enum):
READY = "ready"
ACTIVATED = "activated"
@ -17,6 +17,7 @@ class TradeStoplossType(str, Enum):
TRAILING = "trailing"
class Trade(BaseModel):
id: UUID
validfrom: datetime
status: TradeStatus
direction: TradeDirection
@ -24,4 +25,5 @@ class Trade(BaseModel):
# stoploss_type: TradeStoplossType
stoploss_value: Optional[float] = None
profit: Optional[float] = None
profit_sum: Optional[float] = None

View File

@ -166,6 +166,8 @@ class TradeUpdate(BaseModel):
value: Optional[float]
cash: Optional[float]
pos_avg_price: Optional[float]
profit: Optional[float]
profit_sum: Optional[float]
class RunArchiveChange(BaseModel):

View File

@ -466,6 +466,12 @@ def populate_metrics_output_directory(strat: StrategyInstance):
#filt = max_positions['side'] == 'OrderSide.BUY'
res = dict(zip(max_positions['qty'], max_positions['count']))
#pridani klice obsahujici prescribed trades
try:
res["prescr_trades"]=json.loads(json.dumps(strat.state.vars.prescribedTrades, default=json_serial))
except NameError:
pass
return res
#archives runner and details

View File

@ -242,7 +242,7 @@
</div>
<div class="form-group">
<label for="metrics" class="form-label">Metrics</label>
<textarea class="form-control" rows="3" id="metrics" name="metrics"></textarea>
<textarea class="form-control" rows="8" id="metrics" name="metrics"></textarea>
</div>
<div class="form-group">
<label for="stratvars" class="form-label">Stratvars</label>

View File

@ -5,7 +5,7 @@ var CHART_SHOW_TEXT = false
// var volumeSeries = null
var markersLine = null
var avgBuyLine = null
//TRANSFORM object returned from RESTA PI get_arch_run_detail
//TRANSFORM object returned from REST API get_arch_run_detail
//to series and markers required by lightweigth chart
//input array object bars = { high: [1,2,3], time: [1,2,3], close: [2,2,2]...}
//output array [{ time: 111, open: 11, high: 33, low: 333, close: 333},..]
@ -100,13 +100,26 @@ function transform_data(data) {
marker = {}
marker["time"] = timestamp;
// marker["position"] = (trade.order.side == "buy") ? "belowBar" : "aboveBar"
marker["position"] = (trade.order.side == "buy") ? "inBar" : "aboveBar"
marker["position"] = (trade.order.side == "buy") ? "aboveBar" : "aboveBar"
marker["color"] = (trade.order.side == "buy") ? "#37cade" : "red"
//marker["shape"] = (trade.order.side == "buy") ? "arrowUp" : "arrowDown"
marker["shape"] = (trade.order.side == "buy") ? "circle" : "arrowDown"
marker["shape"] = (trade.order.side == "buy") ? "arrowUp" : "arrowDown"
//marker["text"] = trade.qty + "/" + trade.price
marker["text"] = CHART_SHOW_TEXT ? trade.qty + "/" + trade.price : trade.qty
marker["text"] = (trade.position_qty == 0) ? "c": marker["text"]
qt_optimized = (trade.qty % 1000 === 0) ? (trade.qty / 1000).toFixed(1) + 'K' : trade.qty
if (CHART_SHOW_TEXT) {
//včetně qty
//marker["text"] = qt_optimized + "@" + trade.price
//bez qty
marker["text"] = trade.price
closed_trade_marker_and_profit = (trade.profit) ? "c" + trade.profit.toFixed(1) + "/" + trade.profit_sum.toFixed(1) : "c"
marker["text"] += (trade.position_qty == 0) ? closed_trade_marker_and_profit : ""
} else {
closed_trade_marker_and_profit = (trade.profit) ? "c" + trade.profit.toFixed(1) + "/" + trade.profit_sum.toFixed(1) : "c"
marker["text"] = (trade.position_qty == 0) ? closed_trade_marker_and_profit : ""
}
markers.push(marker)
//prevedeme iso data na timestampy

View File

@ -27,9 +27,7 @@ class StrategyClassicSL(Strategy):
o: Order = data.order
##nejak to vymyslet, aby se dal poslat cely Trade a serializoval se
self.state.ilog(e="Příchozí BUY notif", msg=o.status, trade=json.loads(json.dumps(data, default=json_serial)))
if o.status == OrderStatus.FILLED or o.status == OrderStatus.CANCELED:
#davame pryc pending
self.state.vars.pending = False
if data.event == TradeEvent.FILL or data.event == TradeEvent.PARTIAL_FILL:
@ -44,9 +42,24 @@ class StrategyClassicSL(Strategy):
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 = (avg_costs-bought_amount)
trade_profit = round((avg_costs-bought_amount),2)
self.state.profit += trade_profit
self.state.ilog(e=f"BUY notif - SHORT PROFIT:{round(float(trade_profit),3)} celkem:{round(float(self.state.profit),3)}", msg=str(data.event), 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.profit = trade_profit
trade.profit_sum = self.state.profit
#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)
self.state.ilog(f"updatnut tradeList o profi {str(tradeData)}")
else:
self.state.ilog(e="BUY: Jde o LONG nakuú nepocitame profit zatim")
@ -56,6 +69,10 @@ class StrategyClassicSL(Strategy):
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=json.loads(json.dumps(data, default=json_serial)))
@ -72,9 +89,24 @@ class StrategyClassicSL(Strategy):
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 = (sold_amount - avg_costs)
trade_profit = round((sold_amount - avg_costs),2)
self.state.profit += trade_profit
self.state.ilog(e=f"SELL notif - PROFIT:{round(float(trade_profit),3)} celkem:{round(float(self.state.profit),3)}", msg=str(data.event), 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.profit = trade_profit
trade.profit_sum = self.state.profit
#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)
self.state.ilog(f"updatnut tradeList o profi {str(tradeData)}")
else:
self.state.ilog(e="SELL: Jde o SHORT nepocitame profit zatim")
@ -90,7 +122,7 @@ class StrategyClassicSL(Strategy):
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 = False
self.state.vars.pending = None
a,p = self.interface.pos()
#pri chybe api nechavame puvodni hodnoty
if a != -1: