strategy trade counter, gui shortcuts, better log
This commit is contained in:
@ -4,7 +4,7 @@ from v2realbot.strategy.base import StrategyState
|
||||
from v2realbot.strategy.StrategyOrderLimitVykladaci import StrategyOrderLimitVykladaci
|
||||
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account, OrderSide
|
||||
from v2realbot.indicators.indicators import ema
|
||||
from v2realbot.utils.utils import ltp, isrising, isfalling,trunc,AttributeDict, zoneNY, price2dec, dict_replace_value, print
|
||||
from v2realbot.utils.utils import ltp, isrising, isfalling,trunc,AttributeDict, zoneNY, price2dec, dict_replace_value, print, safe_get
|
||||
from datetime import datetime
|
||||
from icecream import install, ic
|
||||
#from rich import print
|
||||
@ -51,6 +51,7 @@ stratvars = AttributeDict(maxpozic = 400,
|
||||
slope_lookback = 300,
|
||||
lookback_offset = 20,
|
||||
minimum_slope = -0.05,
|
||||
first_buy_market = False
|
||||
)
|
||||
##toto rozparsovat a strategii spustit stejne jako v main
|
||||
toml_string = """
|
||||
@ -129,6 +130,11 @@ def next(data, state: StrategyState):
|
||||
price = last_price
|
||||
state.ilog(e="BUY Vykladame", msg="first price"+str(price) + "pozic:"+str(vykladka), curve=curve, ema=state.indicators.ema[-1], trend=state.vars.Trend, price=price, vykladka=vykladka)
|
||||
##prvni se vyklada na aktualni cenu, další jdou podle krivky, nula v krivce zvyšuje množství pro následující iteraci
|
||||
|
||||
##VAR - na zaklade conf. muzeme jako prvni posilat MARKET
|
||||
if safe_get(state.vars, "first_buy_market") == True:
|
||||
state.buy(size=qty)
|
||||
else:
|
||||
state.buy_l(price=price, size=qty)
|
||||
print("prvni limitka na aktuální cenu. Další podle křivky", price, qty)
|
||||
for i in range(0,vykladka-1):
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -43,8 +43,8 @@ from v2realbot.common.model import TradeUpdate, Order
|
||||
#from rich import print
|
||||
import threading
|
||||
import asyncio
|
||||
from v2realbot.config import BT_DELAYS, DATA_DIR, FILL_CONDITION_BUY_LIMIT, FILL_CONDITION_SELL_LIMIT, FILL_LOG_SURROUNDING_TRADES, FILL_CONS_TRADES_REQUIRED
|
||||
from v2realbot.utils.utils import AttributeDict, ltp, zoneNY, trunc, count_decimals,print
|
||||
from v2realbot.config import BT_DELAYS, DATA_DIR, BT_FILL_CONDITION_BUY_LIMIT, BT_FILL_CONDITION_SELL_LIMIT, BT_FILL_LOG_SURROUNDING_TRADES, BT_FILL_CONS_TRADES_REQUIRED
|
||||
from v2realbot.utils.utils import AttributeDict, ltp, zoneNY, trunc, count_decimals, print
|
||||
from v2realbot.utils.tlog import tlog
|
||||
from v2realbot.enums.enums import FillCondition
|
||||
from datetime import datetime, timedelta
|
||||
@ -194,7 +194,7 @@ class Backtester:
|
||||
#TEST zkusime to nemazat, jak ovlivni performance
|
||||
#Mazeme, jinak je to hruza
|
||||
#nechavame na konci trady, které muzeme potrebovat pro consekutivni pravidlo
|
||||
del self.btdata[0:index_end-2-FILL_CONS_TRADES_REQUIRED]
|
||||
del self.btdata[0:index_end-2-BT_FILL_CONS_TRADES_REQUIRED]
|
||||
#ic("after delete",len(self.btdata[0:index_end]))
|
||||
|
||||
if changes: return 1
|
||||
@ -235,9 +235,9 @@ class Backtester:
|
||||
#NASTVENI PODMINEK PLNENI
|
||||
fast_fill_condition = i[1] <= o.limit_price
|
||||
slow_fill_condition = i[1] < o.limit_price
|
||||
if FILL_CONDITION_BUY_LIMIT == FillCondition.FAST:
|
||||
if BT_FILL_CONDITION_BUY_LIMIT == FillCondition.FAST:
|
||||
fill_condition = fast_fill_condition
|
||||
elif FILL_CONDITION_BUY_LIMIT == FillCondition.SLOW:
|
||||
elif BT_FILL_CONDITION_BUY_LIMIT == FillCondition.SLOW:
|
||||
fill_condition = slow_fill_condition
|
||||
else:
|
||||
print("unknow fill condition")
|
||||
@ -245,17 +245,17 @@ class Backtester:
|
||||
|
||||
if float(i[0]) > float(order_min_fill_time+BT_DELAYS.limit_order_offset) and fill_condition:
|
||||
consec_cnt += 1
|
||||
if consec_cnt == FILL_CONS_TRADES_REQUIRED:
|
||||
if consec_cnt == BT_FILL_CONS_TRADES_REQUIRED:
|
||||
|
||||
#(1679081919.381649, 27.88)
|
||||
ic(i)
|
||||
fill_time = i[0]
|
||||
fill_price = i[1]
|
||||
print("FILL LIMIT BUY at", fill_time, datetime.fromtimestamp(fill_time).astimezone(zoneNY), "at",i[1])
|
||||
if FILL_LOG_SURROUNDING_TRADES != 0:
|
||||
if BT_FILL_LOG_SURROUNDING_TRADES != 0:
|
||||
#TODO loguru
|
||||
print("FILL SURR TRADES: before",work_range[index-FILL_LOG_SURROUNDING_TRADES:index])
|
||||
print("FILL SURR TRADES: fill and after",work_range[index:index+FILL_LOG_SURROUNDING_TRADES])
|
||||
print("FILL SURR TRADES: before",work_range[index-BT_FILL_LOG_SURROUNDING_TRADES:index])
|
||||
print("FILL SURR TRADES: fill and after",work_range[index:index+BT_FILL_LOG_SURROUNDING_TRADES])
|
||||
break
|
||||
else:
|
||||
consec_cnt = 0
|
||||
@ -266,9 +266,9 @@ class Backtester:
|
||||
#NASTVENI PODMINEK PLNENI
|
||||
fast_fill_condition = i[1] >= o.limit_price
|
||||
slow_fill_condition = i[1] > o.limit_price
|
||||
if FILL_CONDITION_SELL_LIMIT == FillCondition.FAST:
|
||||
if BT_FILL_CONDITION_SELL_LIMIT == FillCondition.FAST:
|
||||
fill_condition = fast_fill_condition
|
||||
elif FILL_CONDITION_SELL_LIMIT == FillCondition.SLOW:
|
||||
elif BT_FILL_CONDITION_SELL_LIMIT == FillCondition.SLOW:
|
||||
fill_condition = slow_fill_condition
|
||||
else:
|
||||
print("unknown fill condition")
|
||||
@ -276,16 +276,16 @@ class Backtester:
|
||||
|
||||
if float(i[0]) > float(order_min_fill_time+BT_DELAYS.limit_order_offset) and fill_condition:
|
||||
consec_cnt += 1
|
||||
if consec_cnt == FILL_CONS_TRADES_REQUIRED:
|
||||
if consec_cnt == BT_FILL_CONS_TRADES_REQUIRED:
|
||||
#(1679081919.381649, 27.88)
|
||||
ic(i)
|
||||
fill_time = i[0]
|
||||
fill_price = i[1]
|
||||
print("FILL LIMIT SELL at", fill_time, datetime.fromtimestamp(fill_time).astimezone(zoneNY), "at",i[1])
|
||||
if FILL_LOG_SURROUNDING_TRADES != 0:
|
||||
if BT_FILL_LOG_SURROUNDING_TRADES != 0:
|
||||
#TODO loguru
|
||||
print("FILL SELL SURR TRADES: before",work_range[index-FILL_LOG_SURROUNDING_TRADES:index])
|
||||
print("FILL SELL SURR TRADES: fill and after",work_range[index:index+FILL_LOG_SURROUNDING_TRADES])
|
||||
print("FILL SELL SURR TRADES: before",work_range[index-BT_FILL_LOG_SURROUNDING_TRADES:index])
|
||||
print("FILL SELL SURR TRADES: fill and after",work_range[index:index+BT_FILL_LOG_SURROUNDING_TRADES])
|
||||
break
|
||||
else:
|
||||
consec_cnt = 0
|
||||
|
||||
@ -3,21 +3,21 @@ from v2realbot.enums.enums import Mode, Account, FillCondition
|
||||
from appdirs import user_data_dir
|
||||
|
||||
|
||||
#how many consecutive trades with the fill price are necessary for LIMIT fill to happen()
|
||||
#how many consecutive trades with the fill price are necessary for LIMIT fill to happen in backtesting
|
||||
#0 - optimistic, every knot high will fill the order
|
||||
#N - N consecutive trades required
|
||||
#not impl.yet
|
||||
#minimum is 1
|
||||
FILL_CONS_TRADES_REQUIRED = 2
|
||||
#during trade execution logs X-surrounding trades of the one that triggers the fill
|
||||
FILL_LOG_SURROUNDING_TRADES = 10
|
||||
#fill condition for limit order
|
||||
BT_FILL_CONS_TRADES_REQUIRED = 2
|
||||
#during bt trade execution logs X-surrounding trades of the one that triggers the fill
|
||||
BT_FILL_LOG_SURROUNDING_TRADES = 10
|
||||
#fill condition for limit order in bt
|
||||
# fast - price has to be equal or bigger <=
|
||||
# slow - price has to be bigger <
|
||||
FILL_CONDITION_BUY_LIMIT = FillCondition.FAST
|
||||
FILL_CONDITION_SELL_LIMIT = FillCondition.FAST
|
||||
BT_FILL_CONDITION_BUY_LIMIT = FillCondition.FAST
|
||||
BT_FILL_CONDITION_SELL_LIMIT = FillCondition.FAST
|
||||
#no print in console
|
||||
QUIET_MODE = False
|
||||
QUIET_MODE = True
|
||||
#backend counter of api requests
|
||||
COUNT_API_REQUESTS = False
|
||||
#stratvars that cannot be changed in gui
|
||||
|
||||
@ -222,12 +222,13 @@ def save_history(id: UUID, st: object, runner: Runner, reason: str = None):
|
||||
#zkousime precist profit z objektu
|
||||
try:
|
||||
profit = st.state.profit
|
||||
trade_count = len(st.tradeList)
|
||||
except Exception as e:
|
||||
profit = str(e)
|
||||
|
||||
for i in db.stratins:
|
||||
if str(i.id) == str(id):
|
||||
i.history += "START:"+str(runner.run_started)+"STOP:"+str(runner.run_stopped)+"ACC:"+runner.run_account.value+"M:"+runner.run_mode.value+"PROFIT:"+str(profit)+ "REASON:" + str(reason)
|
||||
i.history += "START:"+str(runner.run_started)+"STOP:"+str(runner.run_stopped)+"ACC:"+runner.run_account.value+"M:"+runner.run_mode.value+"PROFIT:"+str(round(profit,2))+ "TradeCNT:"+str(trade_count) + "REASON:" + str(reason)
|
||||
#i.history += str(runner.__dict__)+"<BR>"
|
||||
db.save()
|
||||
|
||||
|
||||
Binary file not shown.
@ -12,6 +12,7 @@
|
||||
<link href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" rel="stylesheet">
|
||||
<script src="/static/js/jquery.dataTables.min.js"></script>
|
||||
<link rel="stylesheet" href="/static/main.css">
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/mousetrap/1.4.6/mousetrap.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="main" class="mainConteiner flex-container">
|
||||
|
||||
@ -1,6 +1,35 @@
|
||||
|
||||
API_KEY = localStorage.getItem("api-key")
|
||||
|
||||
//KEY shortcuts
|
||||
Mousetrap.bind('e', function() {
|
||||
$( "#button_edit" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('a', function() {
|
||||
$( "#button_add" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('d', function() {
|
||||
$( "#button_dup" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('c', function() {
|
||||
$( "#button_copy" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('r', function() {
|
||||
$( "#button_run" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('p', function() {
|
||||
$( "#button_pause" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('s', function() {
|
||||
$( "#button_stop" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('j', function() {
|
||||
$( "#button_add_json" ).trigger( "click" );
|
||||
});
|
||||
Mousetrap.bind('x', function() {
|
||||
$( "#button_delete" ).trigger( "click" );
|
||||
});
|
||||
|
||||
//on button
|
||||
function store_api_key(event) {
|
||||
key = document.getElementById("api-key").value;
|
||||
|
||||
@ -62,7 +62,7 @@ function connect(event) {
|
||||
var lines = document.getElementById('lines')
|
||||
var line = document.createElement('div')
|
||||
line.classList.add("line")
|
||||
const newLine = document.createTextNode("-----------------NEXT ITER------------------")
|
||||
const newLine = document.createTextNode("---------------")
|
||||
line.appendChild(newLine)
|
||||
lines.appendChild(line)
|
||||
|
||||
@ -84,8 +84,7 @@ function connect(event) {
|
||||
logcnt++;
|
||||
row = '<div data-toggle="collapse" data-target="#rec'+logcnt+'">'+logLine.time + " " + logLine.event + ' - '+ logLine.message+'</div>'
|
||||
str_row = JSON.stringify(logLine.details, null, 2)
|
||||
|
||||
row_detail = '<div id="rec'+logcnt+'" class="collapse pidi"><pre>' + str_row + '</pre></div>'
|
||||
row_detail = '<div id="rec'+logcnt+'" data-toggle="collapse" data-target="#rec'+logcnt+'"class="collapse pidi"><pre>' + str_row + '</pre></div>'
|
||||
|
||||
var lines = document.getElementById('lines')
|
||||
var line = document.createElement('div')
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
from v2realbot.strategy.base import Strategy
|
||||
from v2realbot.utils.utils import parse_alpaca_timestamp, ltp, AttributeDict,trunc,price2dec, zoneNY, print
|
||||
from v2realbot.utils.utils import parse_alpaca_timestamp, ltp, AttributeDict,trunc,price2dec, zoneNY, print, json_serial
|
||||
from v2realbot.utils.tlog import tlog, tlog_exception
|
||||
from v2realbot.enums.enums import Mode, Order, Account
|
||||
from alpaca.trading.models import TradeUpdate
|
||||
from alpaca.trading.enums import TradeEvent, OrderStatus
|
||||
from v2realbot.indicators.indicators import ema
|
||||
import json
|
||||
#from rich import print
|
||||
from random import randrange
|
||||
from alpaca.common.exceptions import APIError
|
||||
@ -12,13 +13,15 @@ import copy
|
||||
from threading import Event
|
||||
|
||||
|
||||
|
||||
class StrategyOrderLimitVykladaci(Strategy):
|
||||
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) -> None:
|
||||
super().__init__(name, symbol, next, init, account, mode, stratvars, open_rush, close_rush, pe, se)
|
||||
|
||||
async def orderUpdateBuy(self, data: TradeUpdate):
|
||||
o: Order = data.order
|
||||
self.state.ilog(e="Příchozí BUY notifikace", msg="order status:"+o.status, status=o.status, orderid=str(o.id))
|
||||
##nejak to vymyslet, aby se dal poslat cely Trade a serializoval se
|
||||
self.state.ilog(e="Příchozí BUY notifikace", msg="order status:"+o.status, trade=json.loads(json.dumps(data, default=json_serial)))
|
||||
if o.status == OrderStatus.FILLED or o.status == OrderStatus.CANCELED:
|
||||
|
||||
#pokud existuje objednavka v pendingbuys - vyhodime ji
|
||||
@ -68,6 +71,7 @@ class StrategyOrderLimitVykladaci(Strategy):
|
||||
|
||||
async def orderUpdateSell(self, data: TradeUpdate):
|
||||
|
||||
self.state.ilog(e="Příchozí SELL notifikace", msg="order status:"+data.order.status, trade=json.loads(json.dumps(data, default=json_serial)))
|
||||
#PROFIT
|
||||
#profit pocitame z TradeUpdate.price a TradeUpdate.qty - aktualne provedene mnozstvi a cena
|
||||
#naklady vypocteme z prumerne ceny, kterou mame v pozicich
|
||||
@ -117,17 +121,17 @@ class StrategyOrderLimitVykladaci(Strategy):
|
||||
def buy(self, size = None, repeat: bool = False):
|
||||
print("overriden method to size&check maximum ")
|
||||
if int(self.state.positions) >= self.state.vars.maxpozic:
|
||||
self.state.ilog(e="buy Maxim mnozstvi naplneno", curr_positions=self.state.positions)
|
||||
self.state.ilog(e="buy Maxim mnozstvi naplneno", positions=self.state.positions)
|
||||
print("max mnostvi naplneno")
|
||||
return 0
|
||||
if size is None:
|
||||
sizer = self.state.vars.chunk
|
||||
else:
|
||||
sizer = size
|
||||
|
||||
self.state.blockbuy = 1
|
||||
self.state.vars.lastbuyindex = self.state.bars['index'][-1]
|
||||
ic(self.state.blockbuy)
|
||||
ic(self.state.vars.lastbuyindex)
|
||||
self.state.ilog(e="send MARKET buy to if", msg="S:"+str(size), ltp=self.state.interface.get_last_price(self.state.symbol))
|
||||
return self.state.interface.buy(size=sizer)
|
||||
|
||||
def buy_l(self, price: float = None, size = None, repeat: bool = False):
|
||||
@ -139,7 +143,7 @@ class StrategyOrderLimitVykladaci(Strategy):
|
||||
if price is None: price=price2dec((self.state.interface.get_last_price(self.symbol)))
|
||||
ic(price)
|
||||
print("odesilame LIMIT s cenou/qty", price, size)
|
||||
self.state.ilog(e="send buy to if", msg="S:"+str(size)+" P:"+str(price), price=price, size=size)
|
||||
self.state.ilog(e="send LIMIT buy to if", msg="S:"+str(size)+" P:"+str(price), price=price, size=size)
|
||||
order = self.state.interface.buy_l(price=price, size=size)
|
||||
print("ukladame pendingbuys")
|
||||
self.state.vars.pendingbuys[str(order)]=price
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -17,6 +17,8 @@ from v2realbot.interfaces.backtest_interface import BacktestInterface
|
||||
from v2realbot.interfaces.live_interface import LiveInterface
|
||||
from alpaca.trading.enums import OrderSide
|
||||
from v2realbot.backtesting.backtester import Backtester
|
||||
from alpaca.trading.models import TradeUpdate
|
||||
from alpaca.trading.enums import TradeEvent, OrderStatus
|
||||
from threading import Event, current_thread
|
||||
import json
|
||||
|
||||
@ -48,6 +50,7 @@ class Strategy:
|
||||
self.account = account
|
||||
self.key = get_key(mode=self.mode, account=self.account)
|
||||
self.rtqueue = None
|
||||
self.tradeList = []
|
||||
|
||||
|
||||
#TODO predelat na dynamické queues
|
||||
@ -308,7 +311,7 @@ class Strategy:
|
||||
#for order updates from LIVE or BACKTEST
|
||||
#updates are sent only for SYMBOL of strategy
|
||||
|
||||
async def order_updates(self, data):
|
||||
async def order_updates(self, data: TradeUpdate):
|
||||
if self.mode == Mode.LIVE or self.mode == Mode.PAPER:
|
||||
now = datetime.now().timestamp()
|
||||
else:
|
||||
@ -316,10 +319,16 @@ class Strategy:
|
||||
|
||||
print("NOTIFICATION ARRIVED AT:", now)
|
||||
|
||||
#pokud jde o FILL zapisujeme do self.trades a notifikujeme
|
||||
if data.event == TradeEvent.FILL:
|
||||
self.tradeList.append(data)
|
||||
|
||||
##TradeUpdate objekt better?
|
||||
order: Order = data.order
|
||||
if order.side == OrderSide.BUY: await self.orderUpdateBuy(data)
|
||||
if order.side == OrderSide.SELL: await self.orderUpdateSell(data)
|
||||
if order.side == OrderSide.BUY:
|
||||
await self.orderUpdateBuy(data)
|
||||
if order.side == OrderSide.SELL:
|
||||
await self.orderUpdateSell(data)
|
||||
|
||||
async def orderUpdateBuy(self, data):
|
||||
print(data)
|
||||
|
||||
Binary file not shown.
@ -16,6 +16,24 @@ import tomli
|
||||
from v2realbot.config import DATA_DIR, QUIET_MODE
|
||||
import requests
|
||||
from uuid import UUID
|
||||
from enum import Enum
|
||||
#from v2realbot.enums.enums import Order
|
||||
from v2realbot.common.model import Order, TradeUpdate
|
||||
|
||||
def safe_get(collection, key, default=None):
|
||||
"""Get values from a collection without raising errors"""
|
||||
|
||||
try:
|
||||
return collection.get(key, default)
|
||||
except TypeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
return collection[key]
|
||||
except (IndexError, TypeError):
|
||||
pass
|
||||
|
||||
return default
|
||||
|
||||
def send_to_telegram(message):
|
||||
apiToken = '5836666362:AAGPuzwp03tczMQTwTBiHW6VsZZ-1RCMAEE'
|
||||
@ -38,6 +56,12 @@ def json_serial(obj):
|
||||
return obj.timestamp()
|
||||
if isinstance(obj, UUID):
|
||||
return str(obj)
|
||||
if isinstance(obj, Enum):
|
||||
return str(obj)
|
||||
if type(obj) is Order:
|
||||
return obj.__dict__
|
||||
if type(obj) is TradeUpdate:
|
||||
return obj.__dict__
|
||||
raise TypeError (str(obj)+"Type %s not serializable" % type(obj))
|
||||
|
||||
def parse_toml_string(tomlst: str):
|
||||
|
||||
Reference in New Issue
Block a user