BT FILL CONDITIONS a FILL LOG SURROUNDING TRADES
This commit is contained in:
@ -11,34 +11,34 @@ btdata = [(1679081913.290388, 27.8634), (1679081913.68588, 27.865), (1679081913.
|
|||||||
|
|
||||||
from bisect import bisect_left
|
from bisect import bisect_left
|
||||||
|
|
||||||
def get_last_price(time: float, symbol: str = None):
|
# def get_last_price(time: float, symbol: str = None):
|
||||||
"""""
|
# """""
|
||||||
returns equity price in timestamp. Used for validations later.
|
# returns equity price in timestamp. Used for validations later.
|
||||||
TODO: optimalize
|
# TODO: optimalize
|
||||||
"""""
|
# """""
|
||||||
for i in range(len(btdata)):
|
# for i in range(len(btdata)):
|
||||||
#print(btdata[i][0])
|
# #print(btdata[i][0])
|
||||||
#print(i)
|
# #print(i)
|
||||||
if btdata[i][0] >= time:
|
# if btdata[i][0] >= time:
|
||||||
break
|
# break
|
||||||
return btdata[i-1]
|
# return btdata[i-1]
|
||||||
|
|
||||||
def take_closest(myList, myNumber):
|
# def take_closest(myList, myNumber):
|
||||||
"""
|
# """
|
||||||
Assumes myList is sorted. Returns first lower value to the number.
|
# Assumes myList is sorted. Returns first lower value to the number.
|
||||||
"""
|
# """
|
||||||
pos = bisect_left(myList, (myNumber,))
|
# pos = bisect_left(myList, (myNumber,))
|
||||||
if pos == 0:
|
# if pos == 0:
|
||||||
return myList[0]
|
# return myList[0]
|
||||||
# if pos == len(myList):
|
# # if pos == len(myList):
|
||||||
# return myList[-1]
|
# # return myList[-1]
|
||||||
after, afterPrice = myList[pos-1]
|
# after, afterPrice = myList[pos-1]
|
||||||
return after,afterPrice
|
# return after,afterPrice
|
||||||
|
|
||||||
print("bisect price")
|
# print("bisect price")
|
||||||
print(take_closest(btdata, 1679081913.986395))
|
# print(take_closest(btdata, 1679081913.986395))
|
||||||
print("stamdard price")
|
# print("stamdard price")
|
||||||
print(get_last_price(1679081913.986395))
|
# print(get_last_price(1679081913.986395))
|
||||||
|
|
||||||
#(1679081919.018929, 27.87), (1679081919.018932, 27.87), (1679081919.018938, 27.87),
|
#(1679081919.018929, 27.87), (1679081919.018932, 27.87), (1679081919.018938, 27.87),
|
||||||
|
|
||||||
@ -122,61 +122,66 @@ print(get_last_price(1679081913.986395))
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
# def get_index_bisect(myList, time):
|
def get_index_bisect(myList, time):
|
||||||
# """
|
"""
|
||||||
# Assumes myList is sorted. Returns first biggeer value to the number.
|
Assumes myList is sorted. Returns first biggeer value to the number.
|
||||||
# """
|
"""
|
||||||
# pos = bisect_left(myList, (time,))
|
pos = bisect_left(myList, (time,))
|
||||||
# if pos == 0:
|
if pos == 0:
|
||||||
# return myList[0]
|
return myList[0]
|
||||||
# if pos == len(myList):
|
if pos == len(myList):
|
||||||
# return myList[-1]
|
return myList[-1]
|
||||||
# return pos
|
return pos
|
||||||
# #after, afterPrice = myList[pos]
|
#after, afterPrice = myList[pos]
|
||||||
# #return after,afterPrice
|
#return after,afterPrice
|
||||||
|
|
||||||
|
|
||||||
# def get_index(btdata, time: float):
|
def get_index(btdata, time: float):
|
||||||
# index_end = None #
|
index_end = None #
|
||||||
# range_end = time
|
range_end = time
|
||||||
# print("range_end",range_end)
|
print("range_end",range_end)
|
||||||
|
|
||||||
# for i in range(len(btdata)):
|
for i in range(len(btdata)):
|
||||||
# #print(btdata[i][0])
|
#print(btdata[i][0])
|
||||||
# #print(i)
|
#print(i)
|
||||||
# if btdata[i][0] >= range_end:
|
if btdata[i][0] >= range_end:
|
||||||
# index_end = i
|
index_end = i
|
||||||
# break
|
break
|
||||||
|
|
||||||
# print("index_end", index_end)
|
print("index_end", index_end)
|
||||||
# print("oriznuto",btdata[0:index_end+1])
|
print("oriznuto",btdata[0:index_end+1])
|
||||||
# return index_end
|
return index_end
|
||||||
|
|
||||||
# index_end = get_index(btdata, 1679081919.018939)
|
index_end = get_index(btdata, 1679081919.018939)
|
||||||
# print("get_index", index_end)
|
print("get_index", index_end)
|
||||||
# index_end = get_index_bisect(btdata, 1679081919.018939)
|
index_end = get_index_bisect(btdata, 1679081919.018939)
|
||||||
# print("get_index_bisect", index_end)
|
print("get_index_bisect", index_end)
|
||||||
# new_range = btdata[0:index_end+1]
|
new_range = btdata[0:index_end+1]
|
||||||
|
|
||||||
# print("novy rozsah?", len(new_range))
|
print("novy rozsah?", len(new_range))
|
||||||
# print("puvodni pole", len(btdata))
|
print("puvodni pole", len(btdata))
|
||||||
|
|
||||||
# #LIMIT FILL - BUY
|
#LIMIT FILL - BUY
|
||||||
# submitted_at: float = 1679081914.739644
|
submitted_at: float = 1679081914.739644
|
||||||
# limit_price: float = 27.865
|
limit_price: float = 27.865
|
||||||
# fill_time = None
|
fill_time = None
|
||||||
# for i in new_range:
|
for index, i in enumerate(new_range):
|
||||||
# #print(i)
|
#for i in new_range:
|
||||||
# ##najde prvni nejvetsi čas vetsi nez minfill a majici
|
#print(i)
|
||||||
# ## pro LIMITku uděláme nějaký spešl BT_DELAY.LIMIT_OFFSET, aby se nevyplnilo hned jako prvni s touto cenou
|
##najde prvni nejvetsi čas vetsi nez minfill a majici
|
||||||
# ## tzn. o kolik se prumerne vyplni limitka pozdeji
|
## pro LIMITku uděláme nějaký spešl BT_DELAY.LIMIT_OFFSET, aby se nevyplnilo hned jako prvni s touto cenou
|
||||||
# if float(i[0]) > float(float(submitted_at) + float(0.020)) and i[1] <= limit_price:
|
## tzn. o kolik se prumerne vyplni limitka pozdeji
|
||||||
# #(1679081919.381649, 27.88)
|
if float(i[0]) > float(float(submitted_at) + float(0.020)) and i[1] <= limit_price:
|
||||||
# print(i)
|
#(1679081919.381649, 27.88)
|
||||||
# fill_time = i[0]
|
print("pět předtím",new_range[index-5:index])
|
||||||
# print("FILL LIMIT BUY at", fill_time, "at",i[1])
|
print(i)
|
||||||
# break
|
print("pět potom",new_range[index+1:index+6])
|
||||||
# if not fill_time: print("NO FILL for ", limit_price)
|
# print(index)
|
||||||
|
fill_time = i[0]
|
||||||
|
##zalogovat fill time
|
||||||
|
print("FILL LIMIT BUY at", fill_time, "at",i[1])
|
||||||
|
break
|
||||||
|
if not fill_time: print("NO FILL for ", limit_price)
|
||||||
|
|
||||||
# #LIMIT FILL - SELL
|
# #LIMIT FILL - SELL
|
||||||
# for i in new_range:
|
# for i in new_range:
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@ -43,9 +43,10 @@ from v2realbot.common.model import TradeUpdate, Order
|
|||||||
#from rich import print
|
#from rich import print
|
||||||
import threading
|
import threading
|
||||||
import asyncio
|
import asyncio
|
||||||
from v2realbot.config import BT_DELAYS, DATA_DIR
|
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.utils.utils import AttributeDict, ltp, zoneNY, trunc, count_decimals,print
|
||||||
from v2realbot.utils.tlog import tlog
|
from v2realbot.utils.tlog import tlog
|
||||||
|
from v2realbot.enums.enums import FillCondition
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
#import matplotlib.pyplot as plt
|
#import matplotlib.pyplot as plt
|
||||||
@ -192,7 +193,8 @@ class Backtester:
|
|||||||
|
|
||||||
#TEST zkusime to nemazat, jak ovlivni performance
|
#TEST zkusime to nemazat, jak ovlivni performance
|
||||||
#Mazeme, jinak je to hruza
|
#Mazeme, jinak je to hruza
|
||||||
del self.btdata[0:index_end-2]
|
#nechavame na konci trady, které muzeme potrebovat pro consekutivni pravidlo
|
||||||
|
del self.btdata[0:index_end-2-FILL_CONS_TRADES_REQUIRED]
|
||||||
#ic("after delete",len(self.btdata[0:index_end]))
|
#ic("after delete",len(self.btdata[0:index_end]))
|
||||||
|
|
||||||
if changes: return 1
|
if changes: return 1
|
||||||
@ -217,29 +219,63 @@ class Backtester:
|
|||||||
|
|
||||||
if o.order_type == OrderType.LIMIT:
|
if o.order_type == OrderType.LIMIT:
|
||||||
if o.side == OrderSide.BUY:
|
if o.side == OrderSide.BUY:
|
||||||
for i in work_range:
|
for index, i in enumerate(work_range):
|
||||||
#print(i)
|
#print(i)
|
||||||
##najde prvni nejvetsi čas vetsi nez minfill a majici odpovídající cenu
|
##najde prvni nejvetsi čas vetsi nez minfill a majici odpovídající cenu
|
||||||
## pro LIMITku nejspíš přidat BT_DELAY.LIMIT_OFFSET, aby se nevyplnilo hned jako prvni s touto cenou
|
## pro LIMITku nejspíš přidat BT_DELAY.LIMIT_OFFSET, aby se nevyplnilo hned jako prvni s touto cenou
|
||||||
## offest by se pocital od nize nalezeneho casu, zvetsil by ho o LIMIT_OFFSET a zjistil, zda by
|
## offest by se pocital od nize nalezeneho casu, zvetsil by ho o LIMIT_OFFSET a zjistil, zda by
|
||||||
##v novem case doslo take k plneni a tam ho vyplnil. Uvidime az jestli bude aktualni prilis optimisticke.
|
##v novem case doslo take k plneni a tam ho vyplnil. Uvidime az jestli bude aktualni prilis optimisticke.
|
||||||
## TBD zjistit na LIVE jaky je tento offset
|
## TBD zjistit na LIVE jaky je tento offset
|
||||||
if float(i[0]) > float(order_min_fill_time+BT_DELAYS.limit_order_offset) and i[1] <= o.limit_price:
|
|
||||||
|
#TODO pridat pokud je EXECUTION_DEBUG zalogování okolnich tradu (5 z kazde strany) od toho, který triggeroval plnění
|
||||||
|
#TODO pridat jako dalsi nastavovaci atribut pocet tradu po ktere musi byt cena zde (aby to nevyplnil knot high)
|
||||||
|
|
||||||
|
#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:
|
||||||
|
fill_condition = fast_fill_condition
|
||||||
|
elif FILL_CONDITION_BUY_LIMIT == FillCondition.SLOW:
|
||||||
|
fill_condition = slow_fill_condition
|
||||||
|
else:
|
||||||
|
print("unknow fill condition")
|
||||||
|
return -1
|
||||||
|
|
||||||
|
if float(i[0]) > float(order_min_fill_time+BT_DELAYS.limit_order_offset) and fill_condition:
|
||||||
#(1679081919.381649, 27.88)
|
#(1679081919.381649, 27.88)
|
||||||
ic(i)
|
ic(i)
|
||||||
fill_time = i[0]
|
fill_time = i[0]
|
||||||
fill_price = i[1]
|
fill_price = i[1]
|
||||||
print("FILL LIMIT BUY at", fill_time, datetime.fromtimestamp(fill_time).astimezone(zoneNY), "at",i[1])
|
print("FILL LIMIT BUY at", fill_time, datetime.fromtimestamp(fill_time).astimezone(zoneNY), "at",i[1])
|
||||||
|
if FILL_LOG_SURROUNDING_TRADES != 0:
|
||||||
|
#TODO loguru
|
||||||
|
print("FILL SURR TRADES: before",work_range[index-FILL_LOG_SURROUNDING_TRADES:index])
|
||||||
|
print("FILL SURR TRADES: after",work_range[index+1:index+FILL_LOG_SURROUNDING_TRADES+1])
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
for i in work_range:
|
for index, i in enumerate(work_range):
|
||||||
#print(i)
|
#print(i)
|
||||||
if float(i[0]) > float(order_min_fill_time+BT_DELAYS.limit_order_offset) and i[1] >= o.limit_price:
|
#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:
|
||||||
|
fill_condition = fast_fill_condition
|
||||||
|
elif FILL_CONDITION_SELL_LIMIT == FillCondition.SLOW:
|
||||||
|
fill_condition = slow_fill_condition
|
||||||
|
else:
|
||||||
|
print("unknown fill condition")
|
||||||
|
return -1
|
||||||
|
|
||||||
|
if float(i[0]) > float(order_min_fill_time+BT_DELAYS.limit_order_offset) and fill_condition:
|
||||||
#(1679081919.381649, 27.88)
|
#(1679081919.381649, 27.88)
|
||||||
ic(i)
|
ic(i)
|
||||||
fill_time = i[0]
|
fill_time = i[0]
|
||||||
fill_price = i[1]
|
fill_price = i[1]
|
||||||
print("FILL LIMIT SELL at", fill_time, datetime.fromtimestamp(fill_time).astimezone(zoneNY), "at",i[1])
|
print("FILL LIMIT SELL at", fill_time, datetime.fromtimestamp(fill_time).astimezone(zoneNY), "at",i[1])
|
||||||
|
if 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: after",work_range[index+1:index+FILL_LOG_SURROUNDING_TRADES+1])
|
||||||
break
|
break
|
||||||
|
|
||||||
elif o.order_type == OrderType.MARKET:
|
elif o.order_type == OrderType.MARKET:
|
||||||
@ -410,9 +446,9 @@ class Backtester:
|
|||||||
reserved = 0
|
reserved = 0
|
||||||
#with lock:
|
#with lock:
|
||||||
for o in self.open_orders:
|
for o in self.open_orders:
|
||||||
if o.qty == OrderSide.SELL and o.symbol == symbol:
|
if o.side == OrderSide.SELL and o.symbol == symbol and o.canceled_at is None:
|
||||||
reserved += o.qty
|
reserved += o.qty
|
||||||
#print("blokovano v open orders pro sell: ", reserved)
|
print("blokovano v open orders pro sell: ", reserved)
|
||||||
|
|
||||||
if int(self.account[symbol][0]) - reserved - int(size) < 0:
|
if int(self.account[symbol][0]) - reserved - int(size) < 0:
|
||||||
print("not enough shares having",self.account[symbol][0],"reserved",reserved,"available",int(self.account[symbol][0]) - reserved,"selling",size)
|
print("not enough shares having",self.account[symbol][0],"reserved",reserved,"available",int(self.account[symbol][0]) - reserved,"selling",size)
|
||||||
@ -423,10 +459,10 @@ class Backtester:
|
|||||||
reserved = 0
|
reserved = 0
|
||||||
#with lock:
|
#with lock:
|
||||||
for o in self.open_orders:
|
for o in self.open_orders:
|
||||||
if o.qty == OrderSide.BUY:
|
if o.side == OrderSide.BUY and o.canceled_at is None:
|
||||||
cena = o.limit_price if o.limit_price else self.get_last_price(time, o.symbol)
|
cena = o.limit_price if o.limit_price else self.get_last_price(time, o.symbol)
|
||||||
reserved += o.qty * cena
|
reserved += o.qty * cena
|
||||||
#print("blokovano v open orders: ", reserved)
|
print("blokovano v open orders: ", reserved)
|
||||||
|
|
||||||
cena = price if price else self.get_last_price(time, self.symbol)
|
cena = price if price else self.get_last_price(time, self.symbol)
|
||||||
if (self.cash - reserved - float(int(size)*float(cena))) < 0:
|
if (self.cash - reserved - float(int(size)*float(cena))) < 0:
|
||||||
@ -441,6 +477,7 @@ class Backtester:
|
|||||||
status = OrderStatus.ACCEPTED,
|
status = OrderStatus.ACCEPTED,
|
||||||
side=side,
|
side=side,
|
||||||
qty=int(size),
|
qty=int(size),
|
||||||
|
filled_qty=0,
|
||||||
limit_price=(float(price) if price else None))
|
limit_price=(float(price) if price else None))
|
||||||
|
|
||||||
self.open_orders.append(order)
|
self.open_orders.append(order)
|
||||||
|
|||||||
@ -1,7 +1,20 @@
|
|||||||
from alpaca.data.enums import DataFeed
|
from alpaca.data.enums import DataFeed
|
||||||
from v2realbot.enums.enums import Mode, Account
|
from v2realbot.enums.enums import Mode, Account, FillCondition
|
||||||
from appdirs import user_data_dir
|
from appdirs import user_data_dir
|
||||||
|
|
||||||
|
|
||||||
|
#how many consecutive trades with the fill price are necessary for limit fill to happen()
|
||||||
|
#0 - optimistic, every knot high will fill the order
|
||||||
|
#N - N consecutive trades required
|
||||||
|
#not impl.yet
|
||||||
|
FILL_CONS_TRADES_REQUIRED = 0
|
||||||
|
#during trade execution logs X-surrounding trades of the one that triggers the fill
|
||||||
|
FILL_LOG_SURROUNDING_TRADES = 10
|
||||||
|
#fill condition for limit order
|
||||||
|
# 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
|
||||||
#no print in console
|
#no print in console
|
||||||
QUIET_MODE = False
|
QUIET_MODE = False
|
||||||
#backend counter of api requests
|
#backend counter of api requests
|
||||||
|
|||||||
@ -218,9 +218,16 @@ def is_stratin_running(id: UUID):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def save_history(id: UUID, st: object, runner: Runner, reason: str = None):
|
def save_history(id: UUID, st: object, runner: Runner, reason: str = None):
|
||||||
|
|
||||||
|
#zkousime precist profit z objektu
|
||||||
|
try:
|
||||||
|
profit = st.state.profit
|
||||||
|
except Exception as e:
|
||||||
|
profit = str(e)
|
||||||
|
|
||||||
for i in db.stratins:
|
for i in db.stratins:
|
||||||
if str(i.id) == str(id):
|
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:XX" + reason + "<BR>"
|
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 += str(runner.__dict__)+"<BR>"
|
#i.history += str(runner.__dict__)+"<BR>"
|
||||||
db.save()
|
db.save()
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@ -12,6 +12,16 @@ class Order:
|
|||||||
self.filled_time = filled_time
|
self.filled_time = filled_time
|
||||||
self.limit_price = limit_price
|
self.limit_price = limit_price
|
||||||
|
|
||||||
|
|
||||||
|
class FillCondition(Enum):
|
||||||
|
"""
|
||||||
|
Execution settings:
|
||||||
|
fast = pro vyplneni limi orderu musi byt cena stejne
|
||||||
|
slow = vetsi (prip. mensi pro sell)
|
||||||
|
TBD nejspis pridat jeste stredni cestu - musi byt stejna
|
||||||
|
"""
|
||||||
|
FAST = "fast"
|
||||||
|
SLOW = "slow"
|
||||||
class Account(Enum):
|
class Account(Enum):
|
||||||
"""
|
"""
|
||||||
Accounts - keys to config
|
Accounts - keys to config
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@ -334,6 +334,7 @@ class Strategy:
|
|||||||
##kroky po iteraci
|
##kroky po iteraci
|
||||||
def after_iteration(self, item):
|
def after_iteration(self, item):
|
||||||
|
|
||||||
|
#DAT DO VNORENE FUNKCE
|
||||||
##check if real time chart is requested
|
##check if real time chart is requested
|
||||||
##posilame dict s objekty: bars, trades podle cbaru, a dale indicators naplnene time a pripadnymi identifikatory (EMA)
|
##posilame dict s objekty: bars, trades podle cbaru, a dale indicators naplnene time a pripadnymi identifikatory (EMA)
|
||||||
if self.rtqueue is not None:
|
if self.rtqueue is not None:
|
||||||
@ -389,7 +390,9 @@ class Strategy:
|
|||||||
#cleaning iterlog lsit
|
#cleaning iterlog lsit
|
||||||
#TODO pridat cistku i mimo RT blok
|
#TODO pridat cistku i mimo RT blok
|
||||||
self.state.iter_log_list = []
|
self.state.iter_log_list = []
|
||||||
|
else:
|
||||||
|
#mazeme logy pokud neni na ws pozadovano
|
||||||
|
self.state.iter_log_list = []
|
||||||
|
|
||||||
# inicializace poplatna typu strategie (např. u LIMITu dotažení existující limitky)
|
# inicializace poplatna typu strategie (např. u LIMITu dotažení existující limitky)
|
||||||
def strat_init(self):
|
def strat_init(self):
|
||||||
@ -502,4 +505,6 @@ class StrategyState:
|
|||||||
else:
|
else:
|
||||||
row = dict(time=self.time, event=e, message=msg, details=kwargs)
|
row = dict(time=self.time, event=e, message=msg, details=kwargs)
|
||||||
self.iter_log_list.append(row)
|
self.iter_log_list.append(row)
|
||||||
|
row["name"] = self.name
|
||||||
print(row)
|
print(row)
|
||||||
|
#TBD mozna odsud to posilat do nejakeho struct logger jako napr. structlog
|
||||||
|
|||||||
Reference in New Issue
Block a user