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
|
||||
|
||||
def get_last_price(time: float, symbol: str = None):
|
||||
"""""
|
||||
returns equity price in timestamp. Used for validations later.
|
||||
TODO: optimalize
|
||||
"""""
|
||||
for i in range(len(btdata)):
|
||||
#print(btdata[i][0])
|
||||
#print(i)
|
||||
if btdata[i][0] >= time:
|
||||
break
|
||||
return btdata[i-1]
|
||||
# def get_last_price(time: float, symbol: str = None):
|
||||
# """""
|
||||
# returns equity price in timestamp. Used for validations later.
|
||||
# TODO: optimalize
|
||||
# """""
|
||||
# for i in range(len(btdata)):
|
||||
# #print(btdata[i][0])
|
||||
# #print(i)
|
||||
# if btdata[i][0] >= time:
|
||||
# break
|
||||
# return btdata[i-1]
|
||||
|
||||
def take_closest(myList, myNumber):
|
||||
"""
|
||||
Assumes myList is sorted. Returns first lower value to the number.
|
||||
"""
|
||||
pos = bisect_left(myList, (myNumber,))
|
||||
if pos == 0:
|
||||
return myList[0]
|
||||
# if pos == len(myList):
|
||||
# return myList[-1]
|
||||
after, afterPrice = myList[pos-1]
|
||||
return after,afterPrice
|
||||
# def take_closest(myList, myNumber):
|
||||
# """
|
||||
# Assumes myList is sorted. Returns first lower value to the number.
|
||||
# """
|
||||
# pos = bisect_left(myList, (myNumber,))
|
||||
# if pos == 0:
|
||||
# return myList[0]
|
||||
# # if pos == len(myList):
|
||||
# # return myList[-1]
|
||||
# after, afterPrice = myList[pos-1]
|
||||
# return after,afterPrice
|
||||
|
||||
print("bisect price")
|
||||
print(take_closest(btdata, 1679081913.986395))
|
||||
print("stamdard price")
|
||||
print(get_last_price(1679081913.986395))
|
||||
# print("bisect price")
|
||||
# print(take_closest(btdata, 1679081913.986395))
|
||||
# print("stamdard price")
|
||||
# print(get_last_price(1679081913.986395))
|
||||
|
||||
#(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):
|
||||
# """
|
||||
# Assumes myList is sorted. Returns first biggeer value to the number.
|
||||
# """
|
||||
# pos = bisect_left(myList, (time,))
|
||||
# if pos == 0:
|
||||
# return myList[0]
|
||||
# if pos == len(myList):
|
||||
# return myList[-1]
|
||||
# return pos
|
||||
# #after, afterPrice = myList[pos]
|
||||
# #return after,afterPrice
|
||||
def get_index_bisect(myList, time):
|
||||
"""
|
||||
Assumes myList is sorted. Returns first biggeer value to the number.
|
||||
"""
|
||||
pos = bisect_left(myList, (time,))
|
||||
if pos == 0:
|
||||
return myList[0]
|
||||
if pos == len(myList):
|
||||
return myList[-1]
|
||||
return pos
|
||||
#after, afterPrice = myList[pos]
|
||||
#return after,afterPrice
|
||||
|
||||
|
||||
# def get_index(btdata, time: float):
|
||||
# index_end = None #
|
||||
# range_end = time
|
||||
# print("range_end",range_end)
|
||||
def get_index(btdata, time: float):
|
||||
index_end = None #
|
||||
range_end = time
|
||||
print("range_end",range_end)
|
||||
|
||||
# for i in range(len(btdata)):
|
||||
# #print(btdata[i][0])
|
||||
# #print(i)
|
||||
# if btdata[i][0] >= range_end:
|
||||
# index_end = i
|
||||
# break
|
||||
for i in range(len(btdata)):
|
||||
#print(btdata[i][0])
|
||||
#print(i)
|
||||
if btdata[i][0] >= range_end:
|
||||
index_end = i
|
||||
break
|
||||
|
||||
# print("index_end", index_end)
|
||||
# print("oriznuto",btdata[0:index_end+1])
|
||||
# return index_end
|
||||
print("index_end", index_end)
|
||||
print("oriznuto",btdata[0:index_end+1])
|
||||
return index_end
|
||||
|
||||
# index_end = get_index(btdata, 1679081919.018939)
|
||||
# print("get_index", index_end)
|
||||
# index_end = get_index_bisect(btdata, 1679081919.018939)
|
||||
# print("get_index_bisect", index_end)
|
||||
# new_range = btdata[0:index_end+1]
|
||||
index_end = get_index(btdata, 1679081919.018939)
|
||||
print("get_index", index_end)
|
||||
index_end = get_index_bisect(btdata, 1679081919.018939)
|
||||
print("get_index_bisect", index_end)
|
||||
new_range = btdata[0:index_end+1]
|
||||
|
||||
# print("novy rozsah?", len(new_range))
|
||||
# print("puvodni pole", len(btdata))
|
||||
print("novy rozsah?", len(new_range))
|
||||
print("puvodni pole", len(btdata))
|
||||
|
||||
# #LIMIT FILL - BUY
|
||||
# submitted_at: float = 1679081914.739644
|
||||
# limit_price: float = 27.865
|
||||
# fill_time = None
|
||||
# for i in new_range:
|
||||
# #print(i)
|
||||
# ##najde prvni nejvetsi čas vetsi nez minfill a majici
|
||||
# ## pro LIMITku uděláme nějaký spešl BT_DELAY.LIMIT_OFFSET, aby se nevyplnilo hned jako prvni s touto cenou
|
||||
# ## tzn. o kolik se prumerne vyplni limitka pozdeji
|
||||
# if float(i[0]) > float(float(submitted_at) + float(0.020)) and i[1] <= limit_price:
|
||||
# #(1679081919.381649, 27.88)
|
||||
# print(i)
|
||||
# fill_time = i[0]
|
||||
# print("FILL LIMIT BUY at", fill_time, "at",i[1])
|
||||
# break
|
||||
# if not fill_time: print("NO FILL for ", limit_price)
|
||||
#LIMIT FILL - BUY
|
||||
submitted_at: float = 1679081914.739644
|
||||
limit_price: float = 27.865
|
||||
fill_time = None
|
||||
for index, i in enumerate(new_range):
|
||||
#for i in new_range:
|
||||
#print(i)
|
||||
##najde prvni nejvetsi čas vetsi nez minfill a majici
|
||||
## pro LIMITku uděláme nějaký spešl BT_DELAY.LIMIT_OFFSET, aby se nevyplnilo hned jako prvni s touto cenou
|
||||
## tzn. o kolik se prumerne vyplni limitka pozdeji
|
||||
if float(i[0]) > float(float(submitted_at) + float(0.020)) and i[1] <= limit_price:
|
||||
#(1679081919.381649, 27.88)
|
||||
print("pět předtím",new_range[index-5:index])
|
||||
print(i)
|
||||
print("pět potom",new_range[index+1:index+6])
|
||||
# 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
|
||||
# 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
|
||||
import threading
|
||||
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.tlog import tlog
|
||||
from v2realbot.enums.enums import FillCondition
|
||||
from datetime import datetime, timedelta
|
||||
import pandas as pd
|
||||
#import matplotlib.pyplot as plt
|
||||
@ -192,7 +193,8 @@ class Backtester:
|
||||
|
||||
#TEST zkusime to nemazat, jak ovlivni performance
|
||||
#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]))
|
||||
|
||||
if changes: return 1
|
||||
@ -217,29 +219,63 @@ class Backtester:
|
||||
|
||||
if o.order_type == OrderType.LIMIT:
|
||||
if o.side == OrderSide.BUY:
|
||||
for i in work_range:
|
||||
for index, i in enumerate(work_range):
|
||||
#print(i)
|
||||
##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
|
||||
## 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.
|
||||
## 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)
|
||||
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:
|
||||
#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
|
||||
else:
|
||||
for i in work_range:
|
||||
for index, i in enumerate(work_range):
|
||||
#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)
|
||||
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:
|
||||
#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
|
||||
|
||||
elif o.order_type == OrderType.MARKET:
|
||||
@ -410,9 +446,9 @@ class Backtester:
|
||||
reserved = 0
|
||||
#with lock:
|
||||
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
|
||||
#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:
|
||||
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
|
||||
#with lock:
|
||||
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)
|
||||
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)
|
||||
if (self.cash - reserved - float(int(size)*float(cena))) < 0:
|
||||
@ -441,6 +477,7 @@ class Backtester:
|
||||
status = OrderStatus.ACCEPTED,
|
||||
side=side,
|
||||
qty=int(size),
|
||||
filled_qty=0,
|
||||
limit_price=(float(price) if price else None))
|
||||
|
||||
self.open_orders.append(order)
|
||||
|
||||
@ -1,7 +1,20 @@
|
||||
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
|
||||
|
||||
|
||||
#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
|
||||
QUIET_MODE = False
|
||||
#backend counter of api requests
|
||||
|
||||
@ -218,9 +218,16 @@ def is_stratin_running(id: UUID):
|
||||
return False
|
||||
|
||||
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:
|
||||
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>"
|
||||
db.save()
|
||||
|
||||
|
||||
Binary file not shown.
@ -12,6 +12,16 @@ class Order:
|
||||
self.filled_time = filled_time
|
||||
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):
|
||||
"""
|
||||
Accounts - keys to config
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@ -334,6 +334,7 @@ class Strategy:
|
||||
##kroky po iteraci
|
||||
def after_iteration(self, item):
|
||||
|
||||
#DAT DO VNORENE FUNKCE
|
||||
##check if real time chart is requested
|
||||
##posilame dict s objekty: bars, trades podle cbaru, a dale indicators naplnene time a pripadnymi identifikatory (EMA)
|
||||
if self.rtqueue is not None:
|
||||
@ -389,7 +390,9 @@ class Strategy:
|
||||
#cleaning iterlog lsit
|
||||
#TODO pridat cistku i mimo RT blok
|
||||
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)
|
||||
def strat_init(self):
|
||||
@ -502,4 +505,6 @@ class StrategyState:
|
||||
else:
|
||||
row = dict(time=self.time, event=e, message=msg, details=kwargs)
|
||||
self.iter_log_list.append(row)
|
||||
row["name"] = self.name
|
||||
print(row)
|
||||
#TBD mozna odsud to posilat do nejakeho struct logger jako napr. structlog
|
||||
|
||||
Reference in New Issue
Block a user