first commit
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/.DS_Store
|
||||
/v2realbot.egg-info
|
||||
34
.gtignore
Normal file
34
.gtignore
Normal file
@ -0,0 +1,34 @@
|
||||
/__pycache__/
|
||||
/backtestresults/
|
||||
/cache/
|
||||
/v2realbot/backtestresults/
|
||||
/v2realbot/cache/
|
||||
/v2realbot/__pycache__/
|
||||
/.vscode/
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
18
.vscode/launch.json
vendored
Normal file
18
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
// Pro informace o možných atributech použijte technologii IntelliSense.
|
||||
// Umístěním ukazatele myši zobrazíte popisy existujících atributů.
|
||||
// Další informace najdete tady: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Python: Aktuální soubor",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"env": {"PYTHONPATH": "${workspaceFolder}:${workspaceFolder}/bld"},
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true
|
||||
}
|
||||
]
|
||||
}
|
||||
6
.vscode/settings.json
vendored
Normal file
6
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"python.analysis.logLevel": "Trace",
|
||||
"terminal.integrated.env.osx": {
|
||||
"PYTHONPATH": "${workspaceFolder}/"
|
||||
}
|
||||
}
|
||||
96
requirements.txt
Normal file
96
requirements.txt
Normal file
@ -0,0 +1,96 @@
|
||||
alpaca==1.0.0
|
||||
alpaca-py==0.7.1
|
||||
altair==4.2.2
|
||||
anyio==3.6.2
|
||||
appdirs==1.4.4
|
||||
asttokens==2.2.1
|
||||
attrs==22.2.0
|
||||
better-exceptions==0.3.3
|
||||
bleach==6.0.0
|
||||
blinker==1.5
|
||||
cachetools==5.3.0
|
||||
certifi==2022.12.7
|
||||
chardet==5.1.0
|
||||
charset-normalizer==3.0.1
|
||||
click==8.1.3
|
||||
contourpy==1.0.7
|
||||
cycler==0.11.0
|
||||
dash==2.9.1
|
||||
dash-bootstrap-components==1.4.1
|
||||
dash-core-components==2.0.0
|
||||
dash-html-components==2.0.0
|
||||
dash-table==5.0.0
|
||||
decorator==5.1.1
|
||||
entrypoints==0.4
|
||||
executing==1.2.0
|
||||
fastapi==0.95.0
|
||||
Flask==2.2.3
|
||||
fonttools==4.39.0
|
||||
gitdb==4.0.10
|
||||
GitPython==3.1.31
|
||||
h11==0.14.0
|
||||
icecream==2.1.3
|
||||
idna==3.4
|
||||
importlib-metadata==6.1.0
|
||||
itsdangerous==2.1.2
|
||||
jsonschema==4.17.3
|
||||
kiwisolver==1.4.4
|
||||
Markdown==3.4.3
|
||||
markdown-it-py==2.2.0
|
||||
MarkupSafe==2.1.2
|
||||
mdurl==0.1.2
|
||||
msgpack==1.0.4
|
||||
newtulipy==0.4.6
|
||||
numpy==1.24.2
|
||||
packaging==23.0
|
||||
pandas==1.5.3
|
||||
param==1.13.0
|
||||
Pillow==9.4.0
|
||||
plotly==5.13.1
|
||||
proto-plus==1.22.2
|
||||
protobuf==3.20.3
|
||||
pyarrow==11.0.0
|
||||
pyasn1==0.4.8
|
||||
pyasn1-modules==0.2.8
|
||||
pyct==0.5.0
|
||||
pydantic==1.10.5
|
||||
pydeck==0.8.0
|
||||
Pygments==2.14.0
|
||||
Pympler==1.0.1
|
||||
pyparsing==3.0.9
|
||||
pyrsistent==0.19.3
|
||||
pysos==1.3.0
|
||||
python-dateutil==2.8.2
|
||||
python-dotenv==1.0.0
|
||||
pytz==2022.7.1
|
||||
pytz-deprecation-shim==0.1.0.post0
|
||||
pyviz-comms==2.2.1
|
||||
PyYAML==6.0
|
||||
requests==2.28.2
|
||||
rich==13.3.1
|
||||
rsa==4.9
|
||||
seaborn==0.12.2
|
||||
semver==2.13.0
|
||||
six==1.16.0
|
||||
smmap==5.0.0
|
||||
sniffio==1.3.0
|
||||
sseclient-py==1.7.2
|
||||
starlette==0.26.1
|
||||
streamlit==1.20.0
|
||||
structlog==23.1.0
|
||||
tenacity==8.2.2
|
||||
toml==0.10.2
|
||||
tomli==2.0.1
|
||||
toolz==0.12.0
|
||||
tornado==6.2
|
||||
tqdm==4.65.0
|
||||
typing_extensions==4.5.0
|
||||
tzdata==2023.2
|
||||
tzlocal==4.3
|
||||
urllib3==1.26.14
|
||||
uvicorn==0.21.1
|
||||
validators==0.20.0
|
||||
webencodings==0.5.1
|
||||
websockets==10.4
|
||||
Werkzeug==2.2.3
|
||||
zipp==3.15.0
|
||||
9
setup.py
Normal file
9
setup.py
Normal file
@ -0,0 +1,9 @@
|
||||
from setuptools import find_packages, setup
|
||||
|
||||
setup(name='v2realbot',
|
||||
version='0.9',
|
||||
description='Realbot trader',
|
||||
author='David Brazda',
|
||||
author_email='davidbrazda61@gmail.com',
|
||||
packages=find_packages()
|
||||
)
|
||||
27
testy/archive/.gtignore
Normal file
27
testy/archive/.gtignore
Normal file
@ -0,0 +1,27 @@
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
# C extensions
|
||||
*.so
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
cache/
|
||||
BIN
testy/archive/__pycache__/config.cpython-311.pyc
Normal file
BIN
testy/archive/__pycache__/config.cpython-311.pyc
Normal file
Binary file not shown.
52
testy/archive/alpacaGetHistoryBars.py
Normal file
52
testy/archive/alpacaGetHistoryBars.py
Normal file
@ -0,0 +1,52 @@
|
||||
from alpaca.data.historical import CryptoHistoricalDataClient, StockHistoricalDataClient
|
||||
from alpaca.data.requests import CryptoLatestTradeRequest, StockLatestTradeRequest, StockLatestBarRequest, StockTradesRequest, StockBarsRequest
|
||||
from alpaca.data.enums import DataFeed
|
||||
from config import API_KEY, SECRET_KEY, MAX_BATCH_SIZE
|
||||
import datetime
|
||||
import time
|
||||
from alpaca.data import Quote, Trade, Snapshot, Bar
|
||||
from alpaca.data.models import BarSet, QuoteSet, TradeSet
|
||||
from alpaca.data.timeframe import TimeFrame
|
||||
import mplfinance as mpf
|
||||
import pandas as pd
|
||||
|
||||
parametry = {}
|
||||
|
||||
# no keys required
|
||||
#client = CryptoHistoricalDataClient()
|
||||
client = StockHistoricalDataClient(API_KEY, SECRET_KEY, raw_data=False)
|
||||
datetime_object_from = datetime.datetime(2023, 2, 27, 18, 51, 38, tzinfo=datetime.timezone.utc)
|
||||
datetime_object_to = datetime.datetime(2023, 2, 27, 21, 51, 39, tzinfo=datetime.timezone.utc)
|
||||
bar_request = StockBarsRequest(symbol_or_symbols="BAC",timeframe=TimeFrame.Hour, start=datetime_object_from, end=datetime_object_to, feed=DataFeed.SIP)
|
||||
|
||||
bars = client.get_stock_bars(bar_request).df
|
||||
#bars = bars.drop(['symbol'])
|
||||
|
||||
#print(bars.df.close)
|
||||
bars = bars.tz_convert('America/New_York')
|
||||
print(bars)
|
||||
print(bars.df.columns)
|
||||
#Index(['open', 'high', 'low', 'close', 'volume', 'trade_count', 'vwap'], dtype='object')
|
||||
bars.df.set_index('timestamp', inplace=True)
|
||||
|
||||
mpf.plot(bars.df, # the dataframe containing the OHLC (Open, High, Low and Close) data
|
||||
type='candle', # use candlesticks
|
||||
volume=True, # also show the volume
|
||||
mav=(3,6,9), # use three different moving averages
|
||||
figratio=(3,1), # set the ratio of the figure
|
||||
style='yahoo', # choose the yahoo style
|
||||
title='Prvni chart');
|
||||
|
||||
# #vrací se list od dict
|
||||
# print(bars["BAC"])
|
||||
|
||||
# # k nemu muzeme pristupovat s
|
||||
# dict = bars["BAC"]
|
||||
# print(type(dict))
|
||||
# print(dict[2].timestamp)
|
||||
|
||||
# print(dict[2].close)
|
||||
|
||||
# print(dict[].close)
|
||||
|
||||
|
||||
51
testy/archive/alpacabacktrader/test.py
Normal file
51
testy/archive/alpacabacktrader/test.py
Normal file
@ -0,0 +1,51 @@
|
||||
#udajne working example for using a minute
|
||||
# timeframe in backtrader with alpaca api
|
||||
|
||||
|
||||
import alpaca_backtrader_api
|
||||
import backtrader as bt
|
||||
import pandas as pd
|
||||
from datetime import datetime
|
||||
from strategies.tos_strategy import TOS
|
||||
|
||||
from dotenv import load_dotenv
|
||||
import os
|
||||
load_dotenv()
|
||||
|
||||
api_key = os.getenv('API_KEY_ID')
|
||||
api_secret = os.getenv('API_SECRET')
|
||||
alpaca_paper = os.getenv('ALPACA_PAPER')
|
||||
|
||||
cerebro = bt.Cerebro()
|
||||
cerebro.addstrategy(TOS)
|
||||
|
||||
cerebro.broker.setcash(100000)
|
||||
cerebro.broker.setcommission(commission=0.0)
|
||||
cerebro.addsizer(bt.sizers.PercentSizer, percents=20)
|
||||
|
||||
store = alpaca_backtrader_api.AlpacaStore(
|
||||
key_id=api_key,
|
||||
secret_key=api_secret,
|
||||
paper=alpaca_paper
|
||||
)
|
||||
|
||||
if not alpaca_paper:
|
||||
broker = store.getbroker() # or just alpaca_backtrader_api.AlpacaBroker()
|
||||
cerebro.setbroker(broker)
|
||||
|
||||
DataFactory = store.getdata # or use alpaca_backtrader_api.AlpacaData
|
||||
data0 = DataFactory(
|
||||
dataname='AAPL',
|
||||
timeframe=bt.TimeFrame.TFrame("Minutes"),
|
||||
fromdate=pd.Timestamp('2018-11-15'),
|
||||
todate=pd.Timestamp('2018-11-17'),
|
||||
historical=True)
|
||||
cerebro.adddata(data0)
|
||||
|
||||
#Resampler for 15 minutes
|
||||
cerebro.resampledata(data0,timeframe=bt.TimeFrame.Minutes,compression=15)
|
||||
|
||||
print('Starting Portfolio Value: %.2f' % cerebro.broker.getvalue())
|
||||
cerebro.run()
|
||||
print('Final Portfolio Value: %.2f' % cerebro.broker.getvalue())
|
||||
cerebro.plot()
|
||||
172
testy/archive/alpacaexampleslive.py
Normal file
172
testy/archive/alpacaexampleslive.py
Normal file
@ -0,0 +1,172 @@
|
||||
# použití websocket loaderu v samostatném threadu
|
||||
# v dalsim threadu pak input a cteni globalniho dataframu
|
||||
# a stopnutí websocket loopu
|
||||
|
||||
#import clients
|
||||
from alpaca.data.live import StockDataStream, CryptoDataStream
|
||||
from alpaca.data.enums import DataFeed
|
||||
from config import API_KEY, SECRET_KEY, MAX_BATCH_SIZE
|
||||
from datetime import datetime
|
||||
import pandas as pd
|
||||
import threading
|
||||
|
||||
|
||||
# pripadne parametry pro request
|
||||
# parametry = {
|
||||
# "brand": "Ford",
|
||||
# "model": "Mustang",
|
||||
# "year": 1964
|
||||
# }
|
||||
|
||||
sloupce=["timestamp","price","size","condition"]
|
||||
sloupce_q=["timestamp","ask_size","ask_price","bid_price","bid_ask"]
|
||||
|
||||
# deklarace globalniho df s timeindexem
|
||||
gdf = pd.DataFrame(columns=sloupce, index=pd.to_datetime([]))
|
||||
gdf_q = pd.DataFrame(columns=sloupce_q, index=pd.to_datetime([]))
|
||||
|
||||
|
||||
# # pro komunikaci mezi thready budeme pouzivat globalni variable
|
||||
# # pro zamezeni race condition pouzijeme mutual lock (mutex)
|
||||
# create a lock
|
||||
# lock = threading.Lock()
|
||||
# with lock:
|
||||
# # add to the variable
|
||||
# variable = variable + 10
|
||||
# # release the lock automatically
|
||||
|
||||
prev_timestamp = "new"
|
||||
batch = []
|
||||
batch_q = []
|
||||
seconds_list = []
|
||||
parametry = {}
|
||||
now = datetime.now() # current date and time aware of timezones
|
||||
now = now.astimezone()
|
||||
|
||||
# client musi byt globalni, aby ho druhy thread dokazal stopnout
|
||||
#client = StockDataStream(API_KEY, SECRET_KEY, raw_data=False, websocket_params=parametry, feed=DataFeed.SIP)
|
||||
client = StockDataStream(API_KEY, SECRET_KEY, raw_data=True, websocket_params=parametry, feed=DataFeed.SIP)
|
||||
|
||||
|
||||
|
||||
## thread pro cteni websocketu a plneni glob.dataframu
|
||||
# pozdeji prepsat do samostatne Class WSReader
|
||||
def ws_reader():
|
||||
print("vstup do threadu ws reader")
|
||||
|
||||
#handler pro ws trader data
|
||||
async def data_handler(data):
|
||||
global gdf
|
||||
global batch
|
||||
#record_list = (data.timestamp, data.open,data.high,data.low,data.close)
|
||||
#batch.append(record_list)
|
||||
print(data)
|
||||
|
||||
# kazdou davku pak zapiseme do datasetu
|
||||
if len(batch) == MAX_BATCH_SIZE:
|
||||
## z aktualniho listu batch udelame DataFrame
|
||||
new_df = pd.DataFrame.from_records(data=batch, columns = sloupce)
|
||||
|
||||
## tento dataframe pridame ke globalnimu
|
||||
gdf = pd.concat([gdf,new_df], axis=0, ignore_index=True)
|
||||
batch = []
|
||||
#print(gdf)
|
||||
|
||||
#handler pro ws quote data
|
||||
async def data_handler_q(data):
|
||||
global gdf_q
|
||||
global batch_q
|
||||
global prev_timestamp
|
||||
global seconds_list
|
||||
record_list = (data.timestamp, data.ask_size,data.ask_price,data.bid_price,data.bid_size)
|
||||
|
||||
batch_q.append(record_list)
|
||||
#print(data.ask_size,data.ask_price,data.bid_price,data.bid_size)
|
||||
#print("sestaveni je",sestaveni, "\\n batch ma ", len(batch), "clenu")
|
||||
print(batch_q)
|
||||
|
||||
##max a min hodnota z druhych hodnot listu
|
||||
def max_value(inputlist):
|
||||
return max([sublist[1] for sublist in inputlist])
|
||||
def min_value(inputlist):
|
||||
return min([sublist[1] for sublist in inputlist])
|
||||
def sum_value(inputlist):
|
||||
for sublist in inputlist: print(sublist[-1])
|
||||
return sum([sublist[-1] for sublist in inputlist])
|
||||
|
||||
#pokud jde o stejnou vterinu nebo o prvni zaznam, pridame do pole
|
||||
if (prev_timestamp=="new") or (data.timestamp.second==prev_timestamp.second):
|
||||
print("stejna vterina")
|
||||
seconds_list.append([data.timestamp, data.ask_price, data.ask_size])
|
||||
#print("po appendu",seconds_list)
|
||||
else:
|
||||
print("nova vterina")
|
||||
# dopocitame ohlc
|
||||
print("max", max_value(seconds_list), "min ", min_value(seconds_list), "sum", sum_value(seconds_list), "open", seconds_list[0][1], "close", seconds_list[-1][1])
|
||||
print("-"*40)
|
||||
seconds_list = []
|
||||
seconds_list.append([data.timestamp, data.ask_price, data.ask_size])
|
||||
print(seconds_list)
|
||||
#vypisu z listu
|
||||
|
||||
print("akt.cas",data.timestamp,"minuly cas", prev_timestamp)
|
||||
|
||||
prev_timestamp = data.timestamp
|
||||
|
||||
# kazdou davku pak zapiseme do datasetu
|
||||
if len(batch_q) == MAX_BATCH_SIZE:
|
||||
## z aktualniho listu batch udelame DataFrame
|
||||
new_df = pd.DataFrame.from_records(data=batch_q, columns = sloupce_q)
|
||||
|
||||
## tento dataframe pridame ke globalnimu
|
||||
gdf_q = pd.concat([gdf_q,new_df], axis=0, ignore_index=True)
|
||||
batch_q = []
|
||||
#print(gdf)t
|
||||
|
||||
#client.subscribe_quotes(data_handler, "BAC")
|
||||
#client.subscribe_trades(data_handler, "BAC")
|
||||
#client.subscribe_updated_bars(data_handler, "BAC")
|
||||
|
||||
## taddy to ceka a bezi
|
||||
print("spoustim run")
|
||||
client.run()
|
||||
print("run skoncil")
|
||||
|
||||
|
||||
def user_prompt():
|
||||
print("Tady je druhy thread, kde muzu delat co chci, pripadne ovladat ws loader")
|
||||
while True:
|
||||
delej = input("Vypsat dataframe: [t-trades;q-quotes;e-exit]")
|
||||
if delej == "t": print(gdf)
|
||||
elif delej =="q": print(gdf_q.tail(20))
|
||||
elif delej =="e": break
|
||||
print("bye")
|
||||
client.stop()
|
||||
|
||||
def main():
|
||||
# definujeme thready
|
||||
t1 = threading.Thread(target=ws_reader)
|
||||
#t2 = threading.Thread(target=user_prompt)
|
||||
|
||||
#spustime thready
|
||||
t1.start()#, t2.start()
|
||||
|
||||
# Wait threads to complete
|
||||
t1.join()#, t2.join()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
# tbd jeste si vyzkouset, zda to bez threadu nepujde takto s asynciem
|
||||
# if __name__ == '__main__':
|
||||
# logging.basicConfig(format='%(asctime)s %(levelname)s %(message)s',
|
||||
# level=logging.INFO)
|
||||
|
||||
# logging.log(logging.INFO, 'Starting up...')
|
||||
# try:
|
||||
# loop = asyncio.get_event_loop()
|
||||
# loop.run_until_complete(main())
|
||||
# loop.close()
|
||||
# except KeyboardInterrupt:
|
||||
# pass
|
||||
39
testy/archive/alpacagetlasttrades.py
Normal file
39
testy/archive/alpacagetlasttrades.py
Normal file
@ -0,0 +1,39 @@
|
||||
from alpaca.data.historical import CryptoHistoricalDataClient, StockHistoricalDataClient
|
||||
from alpaca.data.requests import CryptoLatestTradeRequest, StockLatestTradeRequest, StockLatestBarRequest, StockTradesRequest
|
||||
from alpaca.data.enums import DataFeed
|
||||
from config import API_KEY, SECRET_KEY, MAX_BATCH_SIZE
|
||||
import datetime
|
||||
import time
|
||||
|
||||
parametry = {}
|
||||
|
||||
# no keys required
|
||||
#client = CryptoHistoricalDataClient()
|
||||
client = StockHistoricalDataClient(API_KEY, SECRET_KEY, raw_data=True)
|
||||
|
||||
# single symbol request
|
||||
#request_trade_params = StockTradesRequest(symbol_or_symbols="BAC", feed = DataFeed.SIP)
|
||||
#request_last_bar_params = StockLatestBarRequest(symbol_or_symbols="BAC", feed=DataFeed.SIP)
|
||||
|
||||
#2023, 2, 27, 18, 51, 38
|
||||
|
||||
datetime_object_from = datetime.datetime(2023, 2, 26, 17, 51, 38, tzinfo=datetime.timezone.utc)
|
||||
|
||||
datetime_object_to = datetime.datetime(2023, 2, 28, 17, 51, 39, tzinfo=datetime.timezone.utc)
|
||||
|
||||
trades_request = StockTradesRequest(symbol_or_symbols="C", feed = DataFeed.SIP, start=datetime_object_from, end=datetime_object_to)
|
||||
#latest_trade = client.get_stock_latest_trade(request_trade_params)
|
||||
#latest_bar = client.get_stock_latest_bar(request_last_bar_params)
|
||||
|
||||
# for i in range(1,1000):
|
||||
# latest_bar = client.get_stock_latest_bar(request_last_bar_params)
|
||||
# data = latest_bar['BAC']
|
||||
# print(data.timestamp,data.trade_count, data.trade_count, data.high, data.low, data.close, data.volume, data.vwap)
|
||||
# time.sleep(1)
|
||||
|
||||
all_trades = client.get_stock_trades(trades_request)
|
||||
# must use symbol to access even though it is single symbol
|
||||
# print("last trade",latest_trade)
|
||||
# print("latest bar",latest_bar)
|
||||
# print("Trades Today", all_trades)
|
||||
print(len(all_trades["C"]))
|
||||
66
testy/archive/alpacapyexampleshistorical.py
Normal file
66
testy/archive/alpacapyexampleshistorical.py
Normal file
@ -0,0 +1,66 @@
|
||||
# 2 clients for historical data StockHistoricalDataClient (needs keys), CryptoHistoricalDataClient
|
||||
# 2 clients for real time data CryptoDataStream, StockDataStream
|
||||
|
||||
|
||||
# naimportuju si daneho clienta
|
||||
from alpaca.data.historical import StockHistoricalDataClient, CryptoHistoricalDataClient
|
||||
|
||||
#pokdu pouzivam historicke data(tzn. REST) tak si naimportuju dany request object
|
||||
from alpaca.data.requests import StockLatestQuoteRequest, StockBarsRequest, StockTradesRequest
|
||||
|
||||
#objekty se kterymi pak pracuju (jsou soucasi package výše, tady jen informačně)
|
||||
from alpaca.data import Quote, Trade, Snapshot, Bar
|
||||
from alpaca.data.models import BarSet, QuoteSet, TradeSet
|
||||
|
||||
|
||||
from config import API_KEY, SECRET_KEY
|
||||
from datetime import datetime, timedelta
|
||||
import pandas as pd
|
||||
import rich
|
||||
|
||||
# vytvorim si clienta
|
||||
stock_client = StockHistoricalDataClient(API_KEY, SECRET_KEY, raw_data=True)
|
||||
crypto_client = CryptoHistoricalDataClient()
|
||||
|
||||
time_from = datetime(2023, 2, 17, 14, 30, 0, 0)
|
||||
time_to = datetime(2023, 2, 17, 14, 30, 1, 0)
|
||||
#print(time_from)
|
||||
|
||||
# vytvorim request objekt
|
||||
#latestQuoteRequest = StockLatestQuoteRequest(symbol_or_symbols=["SPY", "GLD", "TLT"])
|
||||
stockTradeRequest = StockTradesRequest(symbol_or_symbols=["BAC","C","MSFT"], start=time_from,end=time_to)
|
||||
|
||||
#zavolam na clientovi metodu s request objektem, vrací se mi Dict[str, Quote] - obj.Quote pro kazdy symbol
|
||||
#latestQuoteObject = stock_client.get_stock_latest_quote(latestQuoteRequest)
|
||||
tradesResponse = stock_client.get_stock_trades(stockTradeRequest)
|
||||
print(tradesResponse)
|
||||
|
||||
for i in tradesResponse['BAC']:
|
||||
print(i)
|
||||
|
||||
# vrací m to tradeset dict = Trades identifikovane symbolem
|
||||
|
||||
#for
|
||||
|
||||
#access as a list
|
||||
#print(tradesResponse["BAC"])
|
||||
|
||||
# The scope of these changes made to
|
||||
# pandas settings are local to with statement.
|
||||
# with pd.option_context('display.max_rows', None,
|
||||
# 'display.max_columns', None,
|
||||
# 'display.precision', 3,
|
||||
# ):
|
||||
# #convert to dataframe
|
||||
# print(tradesResponse.df)
|
||||
|
||||
# this is the Quote object for
|
||||
#bacquote=latestQuoteObject["SPY"]
|
||||
|
||||
# print(bacquote)
|
||||
#vrati se mi objekt typu LatestQuote
|
||||
# print(type(latestQuoteObject))
|
||||
|
||||
# print(latestQuoteObject)
|
||||
#gld_latest_ask_price = latestQuoteObject["GLD"].ask_price
|
||||
#print(gld_latest_ask_price, latestQuoteObject["GLD"].timestamp)
|
||||
74
testy/archive/alpacawebsocketsimple.py
Normal file
74
testy/archive/alpacawebsocketsimple.py
Normal file
@ -0,0 +1,74 @@
|
||||
|
||||
from alpaca.data.live import StockDataStream, CryptoDataStream
|
||||
from alpaca.trading.stream import TradingStream
|
||||
from alpaca.data.enums import DataFeed
|
||||
from config import API_KEY, SECRET_KEY, MAX_BATCH_SIZE
|
||||
from datetime import datetime
|
||||
import mplfinance as mpf
|
||||
import matplotlib.pyplot as plt
|
||||
import threading
|
||||
# pripadne parametry pro request
|
||||
# parametry = {
|
||||
# "brand": "Ford",
|
||||
# "model": "Mustang",
|
||||
# "year": 1964
|
||||
# }
|
||||
parametry = {}
|
||||
i = 0
|
||||
u = 0
|
||||
async def data_handler1(data):
|
||||
print("HANDLER1")
|
||||
global i
|
||||
i += 1
|
||||
print(data)
|
||||
|
||||
async def data_handler2(data):
|
||||
print("HANDLER2")
|
||||
global u
|
||||
u += 1
|
||||
print(data)
|
||||
|
||||
async def data_handler3(data):
|
||||
print("HANDLER3")
|
||||
global u
|
||||
u += 1
|
||||
print(data)
|
||||
|
||||
# plt.ion()
|
||||
|
||||
# def animate(ival):
|
||||
# # PREPARE DATAFRAME WITH OHLC AND "BUYS" AND "SELLS" HERE
|
||||
|
||||
# apds = [mpf.make_addplot(buys, color='tab:green', ax=ax_buys),
|
||||
# mpf.make_addplot(sells, color='tab:red', ax=ax_sells)]
|
||||
# for ax in axes:
|
||||
# ax.clear()
|
||||
# mpf.plot(df_ohlc, type='candle', addplot=apds, ax=ax_main)
|
||||
# print('a')
|
||||
|
||||
|
||||
#client = CryptoDataStream(API_KEY, SECRET_KEY, raw_data=True, websocket_params=parametry)
|
||||
client1 = TradingStream(API_KEY, SECRET_KEY, paper=True)
|
||||
client1.subscribe_trade_updates(data_handler1)
|
||||
t1 = threading.Thread(target=client1.run)
|
||||
t1.start()
|
||||
print("started1")
|
||||
client2 = TradingStream(API_KEY, SECRET_KEY, paper=True)
|
||||
client2.subscribe_trade_updates(data_handler2)
|
||||
t2 = threading.Thread(target=client2.run)
|
||||
t2.start()
|
||||
client3 = TradingStream(API_KEY, SECRET_KEY, paper=True)
|
||||
client3.subscribe_trade_updates(data_handler3)
|
||||
t3 = threading.Thread(target=client3.run)
|
||||
t3.start()
|
||||
print("started2")
|
||||
print(threading.enumerate())
|
||||
t2.join()
|
||||
t1.join()
|
||||
t3.join()
|
||||
|
||||
# client.subscribe_trades(data_handler, "BTC/USD")
|
||||
# #client.subscribe_quotes(data_handler_ETH, "ETH/USD")
|
||||
# print("pred spustenim runu")
|
||||
# client.run()
|
||||
# print("po spusteni runu - muzu neco delat?")
|
||||
47
testy/archive/async.py
Normal file
47
testy/archive/async.py
Normal file
@ -0,0 +1,47 @@
|
||||
#Asyncio - umoznuje a řídí konkurentni kod (v ramci jednoho vlakna, vice vlaken nebo i procesu - skrz concurrent.futures)
|
||||
|
||||
# async si pri definici oznacim funkci, kterou chci asynchronne volat a ve které muze byt dalsi asynchronni volani(await)
|
||||
# await - označuje mi, že na volanou funkci čekám - pokud je pouzito v tasku pak task vraci pokračování zpět o level výše
|
||||
# create task(asynchr.funkce) - vtakto zavolám paralelni funkci bez cekani a blok pokracuje dal
|
||||
# asyncio.wait - ceka na tas
|
||||
# Here is a list of what you need in order to make your program async:
|
||||
|
||||
# Add async keyword in front of your function declarations to make them awaitable.
|
||||
# Add await keyword when you call your async functions (without it they won’t run).
|
||||
# Create tasks from the async functions you wish to start asynchronously. Also wait for their finish.
|
||||
# Call asyncio.run to start the asynchronous section of your program. Only one per thread.
|
||||
# time.sleep(5) - blokuje, await asyncio.sleep(5) - neblokuje kod(asynchroni task)
|
||||
|
||||
import asyncio
|
||||
|
||||
#tbd pridat logger
|
||||
|
||||
async def makej(pracant, cekani):
|
||||
print("pracant ",pracant, "zacal", "bude cekat",cekani,"sekubd")
|
||||
await asyncio.sleep(cekani)
|
||||
print("pracant ",pracant,"docekal",cekani,"sekund")
|
||||
|
||||
async def main():
|
||||
print("vstup do funkce main")
|
||||
#vytvoreni asynchronnich tasků
|
||||
task1 = asyncio.create_task(makej(1,5))
|
||||
task2 = asyncio.create_task(makej(2,3))
|
||||
task3 = asyncio.create_task(makej(3,1))
|
||||
print("tasky jedou - ted jsme v kodu za jejich spustenim - ale pred await.wait")
|
||||
#budeme cekat na dokonceni tasků
|
||||
await asyncio.wait([task1,task2,task3])
|
||||
print("dočekáno na vysledek po volani await.wait")
|
||||
print("a ted volani funkce standardním synchro způsobem, kdy cekame na vysledek ")
|
||||
#volani funkce standardním synchro způsobem, kdy cekame na vysledek
|
||||
await makej(1,1)
|
||||
await makej(2,1)
|
||||
|
||||
# hlavni volani - run by mela byt jedna pro jedno vlakno
|
||||
#asyncio.run(main())
|
||||
|
||||
|
||||
#feature to convert async to sync
|
||||
#asyncio.get_event_loop().run_until_complete() --nebo je tu tento decorator https://github.com/miyakogi/syncer
|
||||
|
||||
newfeature = asyncio.run(makej(1,1))
|
||||
|
||||
57
testy/archive/changeiterable.py
Normal file
57
testy/archive/changeiterable.py
Normal file
@ -0,0 +1,57 @@
|
||||
# to test change iterable (adding items) while iterating
|
||||
|
||||
class Notif:
|
||||
def __init__(self,time):
|
||||
self.time = time
|
||||
|
||||
open_orders = []
|
||||
|
||||
for i in range(1,10):
|
||||
open_orders.append(Notif(i))
|
||||
|
||||
print("cele pole objektu",open_orders)
|
||||
|
||||
# Here, 'reversed' returns a lazy iterator, so it's performant! reversed(l):
|
||||
|
||||
#musi fungovat removing stare a pridavani novych
|
||||
|
||||
#this list contains all not processed notification, that we try to process during this iteration
|
||||
#if time is not right we leave the message for next iter
|
||||
#if time is right we process the message (- note it can trigger additional open_orders, that are added to queue)
|
||||
|
||||
def process_message(notif: Notif):
|
||||
global open_orders
|
||||
if notif.time % 2 == 0 and notif.time < 300:
|
||||
open_orders.append(Notif(notif.time+50))
|
||||
|
||||
todel = []
|
||||
for i in open_orders:
|
||||
print("*******start iterace polozky", i.time)
|
||||
process_message(i)
|
||||
print("removing element",i.time)
|
||||
todel.append(i)
|
||||
print("*****konec iterace", i.time)
|
||||
print()
|
||||
|
||||
print("to del", todel)
|
||||
#removing processed from the list
|
||||
for i in todel:
|
||||
open_orders.remove(i)
|
||||
|
||||
|
||||
print("cely list po skonceni vseho")
|
||||
for i in open_orders: print(i.time)
|
||||
|
||||
|
||||
|
||||
""""
|
||||
pred iteraci se zavola synchroné
|
||||
EXECUTE open orders(time)
|
||||
- pokusi se vytvorit vsechny otevrene ordery do daneho casu (casu dalsi iterace)
|
||||
- podporuje i volani callbacku a to vcetne pokynu vytvoreneho z pokynu
|
||||
- tento novy pokyn muze byt i take exekuovan pokud se vcetne roundtripu vejde do daneho casu
|
||||
pripadne soucasne vytvoreni i exekuci pokynu
|
||||
|
||||
|
||||
"""
|
||||
|
||||
38
testy/archive/classscope.py
Normal file
38
testy/archive/classscope.py
Normal file
@ -0,0 +1,38 @@
|
||||
from uuid import UUID, uuid4
|
||||
from alpaca.trading.enums import OrderSide, OrderStatus, TradeEvent, OrderClass, OrderType, TimeInForce
|
||||
#from utils import AttributeDict
|
||||
from rich import print
|
||||
import threading
|
||||
#import utils
|
||||
import asyncio
|
||||
|
||||
from typing import Any, Optional, List, Union
|
||||
from datetime import datetime, date
|
||||
from pydantic import BaseModel
|
||||
|
||||
class Order(BaseModel):
|
||||
id: UUID
|
||||
submitted_at: datetime
|
||||
filled_at: Optional[datetime]
|
||||
symbol: str
|
||||
qty: Optional[str]
|
||||
filled_qty: Optional[str]
|
||||
filled_avg_price: Optional[str]
|
||||
side: OrderSide
|
||||
limit_price: Optional[str]
|
||||
|
||||
class TradeUpdate(BaseModel):
|
||||
event: Union[TradeEvent, str]
|
||||
execution_id: Optional[UUID]
|
||||
order: Order
|
||||
timestamp: datetime
|
||||
position_qty: Optional[float]
|
||||
price: Optional[float]
|
||||
qty: Optional[float]
|
||||
|
||||
class User(BaseModel):
|
||||
id: int
|
||||
name = "Jana"
|
||||
|
||||
a = Order(id = uuid4(), submitted_at= datetime.now(), symbol = "BAC", side=OrderSide.BUY)
|
||||
print(a)
|
||||
3
testy/archive/config.py
Normal file
3
testy/archive/config.py
Normal file
@ -0,0 +1,3 @@
|
||||
API_KEY = 'PKGGEWIEYZOVQFDRY70L'
|
||||
SECRET_KEY = 'O5Kt8X4RLceIOvM98i5LdbalItsX7hVZlbPYHy8Y'
|
||||
MAX_BATCH_SIZE = 1
|
||||
5
testy/archive/hello_panel_world.py
Normal file
5
testy/archive/hello_panel_world.py
Normal file
@ -0,0 +1,5 @@
|
||||
import panel as pn
|
||||
app = pn.Column("DDD")
|
||||
# app.servable()
|
||||
# app.append("Nice")
|
||||
app.show()
|
||||
17
testy/archive/interpolace.py
Normal file
17
testy/archive/interpolace.py
Normal file
@ -0,0 +1,17 @@
|
||||
import scipy.interpolate as spi
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
x = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
|
||||
y = [4, 7, 11, 16, 22, 29, 38, 49, 63, 80]
|
||||
|
||||
|
||||
y_interp = spi.interp1d(x, y)
|
||||
|
||||
#find y-value associated with x-value of 13
|
||||
#print(y_interp(13))
|
||||
|
||||
|
||||
#create plot of x vs. y
|
||||
#plt.plot(x, y, '-ob')
|
||||
|
||||
31
testy/archive/multiprocess.py
Normal file
31
testy/archive/multiprocess.py
Normal file
@ -0,0 +1,31 @@
|
||||
# narozdil od asyncio a threadingu ma vetsi rezii na vytvoreni
|
||||
# hodí se pro CPU a GPU narocné úlohy, tnz. treba na strategie, kde kazda strategie = 1 process
|
||||
import multiprocessing
|
||||
import logging
|
||||
|
||||
logger = multiprocessing.log_to_stderr()
|
||||
logger.setLevel(logging.INFO)
|
||||
logger.warning('doomed')
|
||||
|
||||
def do_first():
|
||||
print("Running do_first line 1")
|
||||
print("Running do_first line 2")
|
||||
print("Running do_first line 3")
|
||||
|
||||
def do_second():
|
||||
print("Running do_second line 1")
|
||||
print("Running do_second line 2")
|
||||
print("Running do_second line 3")
|
||||
|
||||
def main():
|
||||
t1 = multiprocessing.Process(target=do_first)
|
||||
t2 = multiprocessing.Process(target=do_second)
|
||||
|
||||
# Start processes
|
||||
t1.start(), t2.start()
|
||||
|
||||
# Wait processes to complete
|
||||
t1.join(), t2.join()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
145
testy/archive/test.ipynb
Normal file
145
testy/archive/test.ipynb
Normal file
@ -0,0 +1,145 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 97,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"ename": "ValidationError",
|
||||
"evalue": "1 validation error for StockBarsRequest\ntimeframe\n instance of TimeFrame expected (type=type_error.arbitrary_type; expected_arbitrary_type=TimeFrame)",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[0;31mValidationError\u001b[0m Traceback (most recent call last)",
|
||||
"Cell \u001b[0;32mIn[97], line 35\u001b[0m\n\u001b[1;32m 30\u001b[0m \u001b[39m#print(time_from)\u001b[39;00m\n\u001b[1;32m 31\u001b[0m \n\u001b[1;32m 32\u001b[0m \u001b[39m# vytvorim request objekt\u001b[39;00m\n\u001b[1;32m 33\u001b[0m \u001b[39m#latestQuoteRequest = StockLatestQuoteRequest(symbol_or_symbols=[\"SPY\", \"GLD\", \"TLT\"])\u001b[39;00m\n\u001b[1;32m 34\u001b[0m stockTradeRequest \u001b[39m=\u001b[39m StockTradesRequest(symbol_or_symbols\u001b[39m=\u001b[39m[\u001b[39m\"\u001b[39m\u001b[39mBAC\u001b[39m\u001b[39m\"\u001b[39m], start\u001b[39m=\u001b[39mtime_from,end\u001b[39m=\u001b[39mtime_to)\n\u001b[0;32m---> 35\u001b[0m stockBarRequest \u001b[39m=\u001b[39m StockBarsRequest(symbol_or_symbols\u001b[39m=\u001b[39;49m[\u001b[39m\"\u001b[39;49m\u001b[39mBAC\u001b[39;49m\u001b[39m\"\u001b[39;49m], start\u001b[39m=\u001b[39;49mtime_from,end\u001b[39m=\u001b[39;49mtime_to, timeframe\u001b[39m=\u001b[39;49m\u001b[39m\"\u001b[39;49m\u001b[39m15s\u001b[39;49m\u001b[39m\"\u001b[39;49m)\n\u001b[1;32m 37\u001b[0m \u001b[39m#zavolam na clientovi metodu s request objektem, vrací se mi Dict[str, Quote] - obj.Quote pro kazdy symbol\u001b[39;00m\n\u001b[1;32m 38\u001b[0m \u001b[39m#latestQuoteObject = stock_client.get_stock_latest_quote(latestQuoteRequest)\u001b[39;00m\n\u001b[1;32m 39\u001b[0m \u001b[39m#tradesResponse = stock_client.get_stock_trades(stockTradeRequest).df\u001b[39;00m\n\u001b[1;32m 40\u001b[0m stocksResponse \u001b[39m=\u001b[39m stock_client\u001b[39m.\u001b[39mget_stock_bars(stockBarRequest)\u001b[39m.\u001b[39mdf\n",
|
||||
"File \u001b[0;32m~/Documents/Development/python/trading/.venv/lib/python3.11/site-packages/alpaca/data/requests.py:45\u001b[0m, in \u001b[0;36mBaseTimeseriesDataRequest.__init__\u001b[0;34m(self, **data)\u001b[0m\n\u001b[1;32m 37\u001b[0m \u001b[39mif\u001b[39;00m (\n\u001b[1;32m 38\u001b[0m \u001b[39m\"\u001b[39m\u001b[39mend\u001b[39m\u001b[39m\"\u001b[39m \u001b[39min\u001b[39;00m data\n\u001b[1;32m 39\u001b[0m \u001b[39mand\u001b[39;00m data[\u001b[39m\"\u001b[39m\u001b[39mend\u001b[39m\u001b[39m\"\u001b[39m] \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m\n\u001b[1;32m 40\u001b[0m \u001b[39mand\u001b[39;00m \u001b[39misinstance\u001b[39m(data[\u001b[39m\"\u001b[39m\u001b[39mend\u001b[39m\u001b[39m\"\u001b[39m], datetime)\n\u001b[1;32m 41\u001b[0m \u001b[39mand\u001b[39;00m data[\u001b[39m\"\u001b[39m\u001b[39mend\u001b[39m\u001b[39m\"\u001b[39m]\u001b[39m.\u001b[39mtzinfo \u001b[39mis\u001b[39;00m \u001b[39mnot\u001b[39;00m \u001b[39mNone\u001b[39;00m\n\u001b[1;32m 42\u001b[0m ):\n\u001b[1;32m 43\u001b[0m data[\u001b[39m\"\u001b[39m\u001b[39mend\u001b[39m\u001b[39m\"\u001b[39m] \u001b[39m=\u001b[39m data[\u001b[39m\"\u001b[39m\u001b[39mend\u001b[39m\u001b[39m\"\u001b[39m]\u001b[39m.\u001b[39mastimezone(pytz\u001b[39m.\u001b[39mutc)\u001b[39m.\u001b[39mreplace(tzinfo\u001b[39m=\u001b[39m\u001b[39mNone\u001b[39;00m)\n\u001b[0;32m---> 45\u001b[0m \u001b[39msuper\u001b[39;49m()\u001b[39m.\u001b[39;49m\u001b[39m__init__\u001b[39;49m(\u001b[39m*\u001b[39;49m\u001b[39m*\u001b[39;49mdata)\n",
|
||||
"File \u001b[0;32m~/Documents/Development/python/trading/.venv/lib/python3.11/site-packages/pydantic/main.py:342\u001b[0m, in \u001b[0;36mpydantic.main.BaseModel.__init__\u001b[0;34m()\u001b[0m\n",
|
||||
"\u001b[0;31mValidationError\u001b[0m: 1 validation error for StockBarsRequest\ntimeframe\n instance of TimeFrame expected (type=type_error.arbitrary_type; expected_arbitrary_type=TimeFrame)"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# 2 clients for historical data StockHistoricalDataClient (needs keys), CryptoHistoricalDataClient\n",
|
||||
"# 2 clients for real time data CryptoDataStream, StockDataStream\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# naimportuju si daneho clienta\n",
|
||||
"from alpaca.data.historical import StockHistoricalDataClient, CryptoHistoricalDataClient\n",
|
||||
"\n",
|
||||
"#pokdu pouzivam historicke data(tzn. REST) tak si naimportuju dany request object\n",
|
||||
"from alpaca.data.requests import StockLatestQuoteRequest, StockBarsRequest, StockTradesRequest\n",
|
||||
"\n",
|
||||
"#objekty se kterymi pak pracuju (jsou soucasi package výše, tady jen informačně)\n",
|
||||
"from alpaca.data import Quote, Trade, Snapshot, Bar\n",
|
||||
"from alpaca.data.models import BarSet, QuoteSet, TradeSet\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"from config import API_KEY, SECRET_KEY\n",
|
||||
"import datetime\n",
|
||||
"import pandas as pd\n",
|
||||
"\n",
|
||||
"# vytvorim si clienta\n",
|
||||
"stock_client = StockHistoricalDataClient(API_KEY, SECRET_KEY, raw_data=False)\n",
|
||||
"\n",
|
||||
"sloupce=[\"symbol\",\"timestamp\",\"exchange\",\"price\",\"size\",\"id\",\"conditions\",\"tape\"]\n",
|
||||
"\n",
|
||||
"# deklarace globalniho df s timeindexem\n",
|
||||
"#gdf = pd.DataFrame(columns=sloupce, index=pd.to_datetime([]))\n",
|
||||
"\n",
|
||||
"time_from = datetime.datetime(2023, 2, 17, 14, 50, 0, 0)\n",
|
||||
"time_to = datetime.datetime(2023, 2, 17, 14, 55, 1, 0)\n",
|
||||
"#print(time_from)\n",
|
||||
"\n",
|
||||
"# vytvorim request objekt\n",
|
||||
"#latestQuoteRequest = StockLatestQuoteRequest(symbol_or_symbols=[\"SPY\", \"GLD\", \"TLT\"])\n",
|
||||
"stockTradeRequest = StockTradesRequest(symbol_or_symbols=[\"BAC\"], start=time_from,end=time_to)\n",
|
||||
"stockBarRequest = StockBarsRequest(symbol_or_symbols=[\"BAC\"], start=time_from,end=time_to, timeframe=\"15s\")\n",
|
||||
"\n",
|
||||
"#zavolam na clientovi metodu s request objektem, vrací se mi Dict[str, Quote] - obj.Quote pro kazdy symbol\n",
|
||||
"#latestQuoteObject = stock_client.get_stock_latest_quote(latestQuoteRequest)\n",
|
||||
"#tradesResponse = stock_client.get_stock_trades(stockTradeRequest).df\n",
|
||||
"stocksResponse = stock_client.get_stock_bars(stockBarRequest).df\n",
|
||||
"\n",
|
||||
"#data = [{'t': '2023-02-17T14:50:00.582845696Z', 'x': 'D', 'p': 34.83, 's': 1, 'c': [' ', 'I'], 'i': 71675642337847, 'z': 'A'}, {'t': '2023-02-17T14:50:00.948229632Z', 'x': 'D', 'p': 34.8383, 's': 10, 'c': [' ', 'I'], 'i': 79371872323411, 'z': 'A'}]\n",
|
||||
"# data = [{ 'conditions': [' ', 'I'],\n",
|
||||
"# 'exchange': 'D',\n",
|
||||
"# 'id': 71675642337847,\n",
|
||||
"# 'price': 34.83,\n",
|
||||
"# 'size': 1.0,\n",
|
||||
"# 'symbol': 'BAC',\n",
|
||||
"# 'tape': 'A',\n",
|
||||
"# 'timestamp': datetime.datetime(2023, 2, 17, 14, 50, 0, 582845, tzinfo=datetime.timezone.utc)}, { 'conditions': [' ', 'I'],\n",
|
||||
"# 'exchange': 'D',\n",
|
||||
"# 'id': 79371872323411,\n",
|
||||
"# 'price': 34.8383,\n",
|
||||
"# 'size': 10.0,\n",
|
||||
"# 'symbol': 'BAC',\n",
|
||||
"# 'tape': 'A',\n",
|
||||
"# 'timestamp': datetime.datetime(2023, 2, 17, 14, 50, 0, 948229, tzinfo=datetime.timezone.utc)}, { 'conditions': [' ', 'I'],\n",
|
||||
"# 'exchange': 'D',\n",
|
||||
"# 'id': 71675642400306,\n",
|
||||
"# 'price': 34.835,\n",
|
||||
"# 'size': 1.0,\n",
|
||||
"# 'symbol': 'BAC',\n",
|
||||
"# 'tape': 'A',\n",
|
||||
"# 'timestamp': datetime.datetime(2023, 2, 17, 14, 50, 1, 870989, tzinfo=datetime.timezone.utc)}, { 'conditions': [' ', 'I'],\n",
|
||||
"# 'exchange': 'D',\n",
|
||||
"# 'id': 71675642400308,\n",
|
||||
"# 'price': 34.84,\n",
|
||||
"# 'size': 100.0,\n",
|
||||
"# 'symbol': 'BAC',\n",
|
||||
"# 'tape': 'A',\n",
|
||||
"# 'timestamp': datetime.datetime(2023, 2, 17, 14, 55, 0, 88460, tzinfo=datetime.timezone.utc)}]\n",
|
||||
"# datetime.datetime(2023, 2, 17, 14, 50, 0, 948229, tzinfo=datetime.timezone.utc)\n",
|
||||
"#data = tradesResponse\n",
|
||||
"\n",
|
||||
"#gdf = pd.DataFrame.from_dict(data=data, orient='index')\n",
|
||||
"#gdf = pd.DataFrame(data)\n",
|
||||
"\n",
|
||||
"#gdf = pd.DataFrame(tradesResponse.data[\"BAC\"])\n",
|
||||
"\n",
|
||||
"# works with raw data\n",
|
||||
"#gdf = pd.DataFrame([t for t in tradesResponse[\"BAC\"]], columns=sloupce)\n",
|
||||
"\n",
|
||||
"#gdf = tradesResponse.df\n",
|
||||
"print(stocksResponse)\n",
|
||||
"# print(tradesResponse)\n",
|
||||
"#print(tradesResponse[\"BAC\"])\n",
|
||||
"# print(tradesResponse.data[\"BAC\"])\n",
|
||||
"\n",
|
||||
"# positions_df = pd.concat((pd.DataFrame(position).set_index(0) for position in positions),axis=1)\n",
|
||||
"# positions_df = positions_df.T.apply(pd.to_numeric, errors='ignore').T # convert strings to numeric\n",
|
||||
"# For orders:\n",
|
||||
"# orders_df = pd.concat((pd.DataFrame(order).set_index(0) for order in orders),axis=1).T\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": ".venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.10"
|
||||
},
|
||||
"orig_nbformat": 4,
|
||||
"vscode": {
|
||||
"interpreter": {
|
||||
"hash": "9391835cf7167c62e8e53032533e4da7e63c83f818ef5f19912128bc45706236"
|
||||
}
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
||||
66
testy/archive/test.py
Normal file
66
testy/archive/test.py
Normal file
@ -0,0 +1,66 @@
|
||||
from alpaca.data.live import StockDataStream
|
||||
from alpaca.common.enums import BaseURL
|
||||
import datetime
|
||||
import pandas as pd
|
||||
from alpaca.data.models import Bar, Quote, Trade
|
||||
import csv
|
||||
from config import API_KEY, SECRET_KEY
|
||||
|
||||
key = 'PKHVMXQA09IVXALL92JR'
|
||||
secret = 'FmPwQRFIl7jhLRrXee0Ui73zM9NmAf5O4VH2tyAf'
|
||||
|
||||
# keys required for stock historical data client
|
||||
#client = StockHistoricalDataClient(key, secret)
|
||||
|
||||
# keys required
|
||||
client = StockDataStream(api_key=API_KEY,secret_key=SECRET_KEY)
|
||||
|
||||
df_glob = pd.DataFrame(columns=['timestamp','symbol', 'exchange','size','price','id','conditions','tape'])
|
||||
|
||||
file = open('Trades.txt', 'w')
|
||||
|
||||
# async handler
|
||||
async def quote_data_handler(data):
|
||||
#global df_glob
|
||||
#f_loc = pd.DataFrame(data)
|
||||
#df_glob = df_glob.append(df_loc, ignore_index=True)
|
||||
# quote data will arrive here
|
||||
print(data)
|
||||
ne = str(data) + "\n"
|
||||
file.write(ne)
|
||||
#print(data.timestamp,data.symbol, data.price, data.size, data.exchange, data.id, data.conditios,tape)
|
||||
print("-"*40)
|
||||
|
||||
#client.subscribe_updated_bars(quote_data_handler, "BAC")
|
||||
#client.subscribe_quotes(quote_data_handler, "BAC")
|
||||
client.subscribe_trades(quote_data_handler, "BAC")
|
||||
|
||||
print("pred spustenim run")
|
||||
try:
|
||||
client.run()
|
||||
#print(df)
|
||||
except Exception as err:
|
||||
print(f"{type(err).__name__} was raised: {err}")
|
||||
print("globalni dataframe")
|
||||
print(df_glob)
|
||||
file.close()
|
||||
|
||||
print(df_glob)
|
||||
|
||||
# timestamp symbol exchange size price id conditions tape 0 1
|
||||
# 0 NaN NaN NaN NaN NaN NaN NaN NaN symbol BAC
|
||||
# 1 NaN NaN NaN NaN NaN NaN NaN NaN timestamp 2023-02-15 19:47:19.430511+00:00
|
||||
# 2 NaN NaN NaN NaN NaN NaN NaN NaN exchange V
|
||||
# 3 NaN NaN NaN NaN NaN NaN NaN NaN price 35.52
|
||||
# 4 NaN NaN NaN NaN NaN NaN NaN NaN size 50.0
|
||||
# .. ... ... ... ... ... ... ... ... ... ...
|
||||
# 59 NaN NaN NaN NaN NaN NaN NaN NaN price 35.51
|
||||
# 60 NaN NaN NaN NaN NaN NaN NaN NaN size 7.0
|
||||
# 61 NaN NaN NaN NaN NaN NaN NaN NaN id 56493486924086
|
||||
# 62 NaN NaN NaN NaN NaN NaN NaN NaN conditions [ , I]
|
||||
# 63 NaN NaN NaN NaN NaN NaN NaN NaN tape A
|
||||
|
||||
order_data_json = request.get_json()
|
||||
|
||||
# validate data
|
||||
MarketOrderRequest(**order_data_json)
|
||||
1
testy/archive/testqueue.py
Normal file
1
testy/archive/testqueue.py
Normal file
@ -0,0 +1 @@
|
||||
|
||||
35
testy/archive/threadcondition.py
Normal file
35
testy/archive/threadcondition.py
Normal file
@ -0,0 +1,35 @@
|
||||
# A condition variable allows one or more threads to wait until they are
|
||||
# notified by another thread.
|
||||
|
||||
import threading
|
||||
import time
|
||||
import logging
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG,
|
||||
format='(%(threadName)-9s) %(message)s',)
|
||||
|
||||
def consumer(cv):
|
||||
logging.debug('Consumer thread started ...')
|
||||
with cv:
|
||||
logging.debug('Consumer waiting ...')
|
||||
cv.wait()
|
||||
logging.debug('Consumer consumed the resource')
|
||||
|
||||
def producer(cv):
|
||||
logging.debug('Producer thread started ...')
|
||||
with cv:
|
||||
logging.debug('Making resource available')
|
||||
logging.debug('Notifying to all consumers')
|
||||
cv.notify_all()
|
||||
|
||||
if __name__ == '__main__':
|
||||
condition = threading.Condition()
|
||||
cs1 = threading.Thread(name='consumer1', target=consumer, args=(condition,))
|
||||
cs2 = threading.Thread(name='consumer2', target=consumer, args=(condition,))
|
||||
pd = threading.Thread(name='producer', target=producer, args=(condition,))
|
||||
|
||||
cs1.start()
|
||||
time.sleep(2)
|
||||
cs2.start()
|
||||
time.sleep(2)
|
||||
pd.start()
|
||||
54
testy/archive/thready.py
Normal file
54
testy/archive/thready.py
Normal file
@ -0,0 +1,54 @@
|
||||
# pouziti threadu - narozdil od asyncio - nemame pod tim uplnou kontrolu a ridi to knihovna
|
||||
# thready jsou výhodne pro naročné IO operace, např. loadery, requestory, scrapery, ukladače atp.
|
||||
# how to share data between Threads
|
||||
# 1.Sharing a boolean variable with a threading.Event.
|
||||
# declare in unset or false state
|
||||
# event = threading.Event()
|
||||
# if event.is_set(): # check if set
|
||||
# event.set() # set the event true
|
||||
# event.clear() # or false
|
||||
|
||||
# 2.Protecting global shared data with a threading.Lock.
|
||||
# lock = threading.Lock()
|
||||
# with lock:
|
||||
# variable = variable + 10
|
||||
|
||||
# 3.Sharing data with a queue.Queue. Queue can be shared between threads.
|
||||
# create a queue
|
||||
# queue = Queue() #create FIFO
|
||||
# queue.put(i) #enque
|
||||
# data = queue.get() #dequeue
|
||||
|
||||
# dale je tu condition - takova roura mezi consumerem a producerem
|
||||
# cond = threading.Condition()
|
||||
# cond.wait() #consumer waiting
|
||||
# cond.notifyAll() #producer notifiying consumer, they can continue
|
||||
# consumer threads wait for the Condition to be set before continuing.
|
||||
# The producer thread is responsible for setting the condition and notifying the other threads
|
||||
# that they can continue. Více v sam.test filu.
|
||||
|
||||
|
||||
import threading
|
||||
|
||||
def do_first():
|
||||
print("Running do_first line 1")
|
||||
print("Running do_first line 2")
|
||||
print("Running do_first line 3")
|
||||
|
||||
def do_second():
|
||||
print("Running do_second line 1")
|
||||
print("Running do_second line 2")
|
||||
print("Running do_second line 3")
|
||||
|
||||
def main():
|
||||
t1 = threading.Thread(target=do_first)
|
||||
t2 = threading.Thread(target=do_second)
|
||||
|
||||
# Start threads
|
||||
t1.start(), t2.start()
|
||||
|
||||
# Wait threads to complete
|
||||
t1.join(), t2.join()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
74
testy/changeiterable (kopie).py
Normal file
74
testy/changeiterable (kopie).py
Normal file
@ -0,0 +1,74 @@
|
||||
from uuid import UUID, uuid4
|
||||
from alpaca.trading.enums import OrderSide, OrderStatus, TradeEvent, OrderClass, OrderType, TimeInForce
|
||||
#from utils import AttributeDict
|
||||
from rich import print
|
||||
from typing import Any, Optional, List, Union
|
||||
from datetime import datetime, date
|
||||
from pydantic import BaseModel
|
||||
from common.model import Order
|
||||
# to test change iterable (adding items) while iterating
|
||||
import asyncio
|
||||
|
||||
class Notif:
|
||||
def __init__(self,time):
|
||||
self.time = time
|
||||
|
||||
open_orders: list = []
|
||||
|
||||
for i in range(1,10):
|
||||
open_orders.append(Order(id=uuid4(),
|
||||
submitted_at = datetime.utcnow(),
|
||||
qty=1,
|
||||
order_type=OrderType.MARKET,
|
||||
symbol = "BAC",
|
||||
status = OrderStatus.ACCEPTED,
|
||||
side = OrderSide.BUY))
|
||||
|
||||
print("cele pole objektu",open_orders)
|
||||
|
||||
# Here, 'reversed' returns a lazy iterator, so it's performant! reversed(l):
|
||||
|
||||
#musi fungovat removing stare a pridavani novych
|
||||
|
||||
#this list contains all not processed notification, that we try to process during this iteration
|
||||
#if time is not right we leave the message for next iter
|
||||
#if time is right we process the message (- note it can trigger additional open_orders, that are added to queue)
|
||||
|
||||
async def apenduj():
|
||||
global open_orders
|
||||
open_orders.append("cago")
|
||||
# if notif.time % 2 == 0 and notif.time < 300:
|
||||
# open_orders.append(Notif(notif.time+50))
|
||||
|
||||
todel = []
|
||||
for i in open_orders:
|
||||
#print("*******start iterace polozky", i.time)
|
||||
print(i)
|
||||
print("removing element",i)
|
||||
res = asyncio.run(apenduj())
|
||||
todel.append(i)
|
||||
print("*****konec iterace", i)
|
||||
print()
|
||||
|
||||
print("to del", todel)
|
||||
#removing processed from the list
|
||||
for i in todel:
|
||||
open_orders.remove(i)
|
||||
|
||||
|
||||
print("cely list po skonceni vseho")
|
||||
for i in open_orders: print(i.id)
|
||||
|
||||
|
||||
|
||||
""""
|
||||
pred iteraci se zavola synchroné
|
||||
EXECUTE open orders(time)
|
||||
- pokusi se vytvorit vsechny otevrene ordery do daneho casu (casu dalsi iterace)
|
||||
- podporuje i volani callbacku a to vcetne pokynu vytvoreneho z pokynu
|
||||
- tento novy pokyn muze byt i take exekuovan pokud se vcetne roundtripu vejde do daneho casu
|
||||
pripadne soucasne vytvoreni i exekuci pokynu
|
||||
|
||||
|
||||
"""
|
||||
|
||||
70
testy/changeiterable.py
Normal file
70
testy/changeiterable.py
Normal file
@ -0,0 +1,70 @@
|
||||
from uuid import UUID, uuid4
|
||||
from alpaca.trading.enums import OrderSide, OrderStatus, TradeEvent, OrderClass, OrderType, TimeInForce
|
||||
#from utils import AttributeDict
|
||||
from rich import print
|
||||
from typing import Any, Optional, List, Union
|
||||
from datetime import datetime, date
|
||||
from pydantic import BaseModel
|
||||
from common.model import Order
|
||||
# to test change iterable (adding items) while iterating
|
||||
|
||||
class Notif:
|
||||
def __init__(self,time):
|
||||
self.time = time
|
||||
|
||||
open_orders: list(Order) = []
|
||||
|
||||
for i in range(1,10):
|
||||
open_orders.append(Order(id=uuid4()),
|
||||
submitted_at = datetime.utcnow(),
|
||||
symbol = "BAC",
|
||||
status = OrderStatus.ACCEPTED,
|
||||
side = OrderSide.BUY)
|
||||
|
||||
print("cele pole objektu",open_orders)
|
||||
|
||||
# Here, 'reversed' returns a lazy iterator, so it's performant! reversed(l):
|
||||
|
||||
#musi fungovat removing stare a pridavani novych
|
||||
|
||||
#this list contains all not processed notification, that we try to process during this iteration
|
||||
#if time is not right we leave the message for next iter
|
||||
#if time is right we process the message (- note it can trigger additional open_orders, that are added to queue)
|
||||
|
||||
def process_message(notif: Notif):
|
||||
global open_orders
|
||||
pass
|
||||
# if notif.time % 2 == 0 and notif.time < 300:
|
||||
# open_orders.append(Notif(notif.time+50))
|
||||
|
||||
todel = []
|
||||
for i in open_orders:
|
||||
#print("*******start iterace polozky", i.time)
|
||||
process_message(i.id)
|
||||
print("removing element",i.id)
|
||||
todel.append(i)
|
||||
print("*****konec iterace", i.id)
|
||||
print()
|
||||
|
||||
print("to del", todel)
|
||||
#removing processed from the list
|
||||
for i in todel:
|
||||
open_orders.remove(i)
|
||||
|
||||
|
||||
print("cely list po skonceni vseho")
|
||||
for i in open_orders: print(i.id)
|
||||
|
||||
|
||||
|
||||
""""
|
||||
pred iteraci se zavola synchroné
|
||||
EXECUTE open orders(time)
|
||||
- pokusi se vytvorit vsechny otevrene ordery do daneho casu (casu dalsi iterace)
|
||||
- podporuje i volani callbacku a to vcetne pokynu vytvoreneho z pokynu
|
||||
- tento novy pokyn muze byt i take exekuovan pokud se vcetne roundtripu vejde do daneho casu
|
||||
pripadne soucasne vytvoreni i exekuci pokynu
|
||||
|
||||
|
||||
"""
|
||||
|
||||
9915
testy/dash_backtest_results.py
Normal file
9915
testy/dash_backtest_results.py
Normal file
File diff suppressed because it is too large
Load Diff
151
testy/dash_save_html.py
Normal file
151
testy/dash_save_html.py
Normal file
@ -0,0 +1,151 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
from html.parser import HTMLParser
|
||||
|
||||
import dash
|
||||
import pandas as pd
|
||||
import plotly.express as px
|
||||
import requests
|
||||
from dash import html, dcc, dash_table, Input, Output
|
||||
|
||||
|
||||
def patch_file(file_path: str, content: bytes, extra: dict = None) -> bytes:
|
||||
if file_path == 'index.html':
|
||||
index_html_content = content.decode('utf8')
|
||||
extra_jsons = f'''
|
||||
var patched_jsons_content={{
|
||||
{','.join(["'/" + k + "':" + v.decode("utf8") + "" for k, v in extra.items()])}
|
||||
}};
|
||||
'''
|
||||
patched_content = index_html_content.replace(
|
||||
'<footer>',
|
||||
f'''
|
||||
<footer>
|
||||
<script>
|
||||
''' + extra_jsons + '''
|
||||
const origFetch = window.fetch;
|
||||
window.fetch = function () {
|
||||
const e = arguments[0]
|
||||
if (patched_jsons_content.hasOwnProperty(e)) {
|
||||
return Promise.resolve({
|
||||
json: () => Promise.resolve(patched_jsons_content[e]),
|
||||
headers: new Headers({'content-type': 'application/json'}),
|
||||
status: 200,
|
||||
});
|
||||
} else {
|
||||
return origFetch.apply(this, arguments)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
'''
|
||||
).replace(
|
||||
'href="/',
|
||||
'href="'
|
||||
).replace(
|
||||
'src="/',
|
||||
'src="'
|
||||
)
|
||||
return patched_content.encode('utf8')
|
||||
else:
|
||||
return content
|
||||
|
||||
|
||||
def write_file(file_path: str, content: bytes, target_dir='target', ):
|
||||
target_file_path = os.path.join(target_dir, file_path.lstrip('/').split('?')[0])
|
||||
target_leaf_dir = os.path.dirname(target_file_path)
|
||||
os.makedirs(target_leaf_dir, exist_ok=True)
|
||||
with open(target_file_path, 'wb') as f:
|
||||
f.write(content)
|
||||
pass
|
||||
|
||||
|
||||
class ExternalResourceParser(HTMLParser):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.resources = []
|
||||
|
||||
def handle_starttag(self, tag, attrs):
|
||||
if tag == 'link':
|
||||
for k, v in attrs:
|
||||
if k == 'href':
|
||||
self.resources.append(v)
|
||||
if tag == 'script':
|
||||
for k, v in attrs:
|
||||
if k == 'src':
|
||||
self.resources.append(v)
|
||||
|
||||
|
||||
def make_static(base_url, target_dir='target'):
|
||||
index_html_bytes = requests.get(base_url).content
|
||||
json_paths = ['_dash-layout', '_dash-dependencies', ]
|
||||
extra_json = {}
|
||||
for json_path in json_paths:
|
||||
json_content = requests.get(base_url + json_path).content
|
||||
extra_json[json_path] = json_content
|
||||
|
||||
patched_bytes = patch_file('index.html', index_html_bytes, extra=extra_json)
|
||||
write_file('index.html', patched_bytes, target_dir)
|
||||
parser = ExternalResourceParser()
|
||||
parser.feed(patched_bytes.decode('utf8'))
|
||||
extra_js = [
|
||||
'_dash-component-suites/dash/dcc/async-graph.js',
|
||||
'_dash-component-suites/dash/dcc/async-plotlyjs.js',
|
||||
'_dash-component-suites/dash/dash_table/async-table.js',
|
||||
'_dash-component-suites/dash/dash_table/async-highlight.js'
|
||||
]
|
||||
for resource_url in parser.resources + extra_js:
|
||||
resource_url_full = base_url + resource_url
|
||||
print(f'get {resource_url_full}')
|
||||
resource_bytes = requests.get(resource_url_full).content
|
||||
patched_bytes = patch_file(resource_url, resource_bytes)
|
||||
write_file(resource_url, patched_bytes, target_dir)
|
||||
|
||||
|
||||
def main():
|
||||
port = 9050
|
||||
app = dash.Dash(__name__)
|
||||
df = pd.DataFrame({
|
||||
"Fruit": ["Apples", "Oranges", "Bananas", "Apples", "Oranges", "Bananas"],
|
||||
"Amount": [4, 1, 2, 2, 4, 5],
|
||||
"City": ["SF", "SF", "SF", "Montreal", "Montreal", "Montreal"]
|
||||
})
|
||||
|
||||
fig = px.bar(df, x="Fruit", y="Amount", color="City", barmode="group")
|
||||
|
||||
app.layout = html.Div(children=[
|
||||
html.Button('save static', id='save', n_clicks=0),
|
||||
html.Span('', id='saved'),
|
||||
|
||||
html.H1(children='Hello Dash'),
|
||||
|
||||
html.Div(children='''
|
||||
Dash: A web application framework for your data.
|
||||
'''),
|
||||
|
||||
dcc.Graph(
|
||||
id='example-graph',
|
||||
figure=fig
|
||||
),
|
||||
dash_table.DataTable(
|
||||
id='table',
|
||||
columns=[{"name": i, "id": i} for i in df.columns],
|
||||
data=df.to_dict('records'),
|
||||
)
|
||||
])
|
||||
|
||||
@app.callback(
|
||||
Output('saved', 'children'),
|
||||
Input('save', 'n_clicks'),
|
||||
)
|
||||
def save_result(n_clicks):
|
||||
if n_clicks == 0:
|
||||
return 'not saved'
|
||||
else:
|
||||
make_static(f'http://127.0.0.1:{port}/')
|
||||
return 'saved'
|
||||
|
||||
app.run_server(debug=False, port=port)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
38
testy/debugprints.py
Normal file
38
testy/debugprints.py
Normal file
@ -0,0 +1,38 @@
|
||||
import inspect
|
||||
import re
|
||||
import pprint
|
||||
from rich import print
|
||||
from datetime import datetime
|
||||
|
||||
def d(x, n=None):
|
||||
frame = inspect.currentframe().f_back
|
||||
s = inspect.getframeinfo(frame).code_context[0]
|
||||
print(s)
|
||||
r = re.search(r"\((.*)\)", s).group(1)
|
||||
print("{} = {}".format(r,x), n)
|
||||
|
||||
def prinfo(*args):
|
||||
frame = inspect.currentframe().f_back
|
||||
s = inspect.getframeinfo(frame).code_context[0]
|
||||
r = re.search(r"\((.*)\)", s).group(1)
|
||||
print(r)
|
||||
vnames = r.split(", ")
|
||||
print(vnames)
|
||||
for i,(var,val) in enumerate(zip(vnames, args)):
|
||||
print(f"{var} = {val}")
|
||||
|
||||
def p(var, n = None):
|
||||
if n: print(n, f'{var = }')
|
||||
else: print(f'{var = }')
|
||||
|
||||
a = 34
|
||||
b= dict(a1=123,b2="cus")
|
||||
c = "covece"
|
||||
#p(a)
|
||||
#d(b, "neco")
|
||||
|
||||
p(a)
|
||||
p(a,"neco")
|
||||
prinfo(b,c)
|
||||
|
||||
|
||||
70
testy/decorator_test.py
Normal file
70
testy/decorator_test.py
Normal file
@ -0,0 +1,70 @@
|
||||
import inspect
|
||||
|
||||
class LiveInterface:
|
||||
|
||||
def prepost(f):
|
||||
def prepost_wrapper(self, *args, **kwargs):
|
||||
pre_name = 'pre_' + f.__name__
|
||||
post_name = 'post_' + f.__name__
|
||||
print(dir(self))
|
||||
print(self.__repr__)
|
||||
res = 1
|
||||
if hasattr(self, pre_name):
|
||||
res = getattr(self, pre_name) (*args, **kwargs)
|
||||
if res > 0:
|
||||
ret = f(self, *args, **kwargs)
|
||||
if hasattr(self, post_name): getattr(self, post_name)(*args, **kwargs)
|
||||
return ret
|
||||
else:
|
||||
print("plugin vratil zaporné. Skipping")
|
||||
return res
|
||||
return prepost_wrapper
|
||||
|
||||
|
||||
def __init__(self) -> None:
|
||||
pass
|
||||
|
||||
@prepost
|
||||
def buy(self):
|
||||
print("buy")
|
||||
|
||||
##
|
||||
# class NewInterface(LiveInterface):
|
||||
# def __init__(self) -> None:
|
||||
# super().__init__()
|
||||
|
||||
|
||||
|
||||
class Strategy():
|
||||
def __init__(self) -> None:
|
||||
#tady is prepnu na live or bt
|
||||
self.interface = Strategy.StrategyInterface()
|
||||
self.neco = 1
|
||||
#self.interface.buy()
|
||||
#self.interface = LiveInterface()
|
||||
#self.interface.buy = self.buy_more
|
||||
self.interface.buy()
|
||||
|
||||
|
||||
|
||||
class StrategyInterface(LiveInterface):
|
||||
def __init__(self) -> None:
|
||||
super().__init__()
|
||||
|
||||
def pre_buy(self):
|
||||
print("prebuy")
|
||||
return 3
|
||||
|
||||
def post_buy(self):
|
||||
print("postbuy")
|
||||
return -2
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
a = Strategy()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
##
|
||||
34
testy/gethistorytrades.py
Normal file
34
testy/gethistorytrades.py
Normal file
@ -0,0 +1,34 @@
|
||||
import os,sys
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
print(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from alpaca.data.historical import CryptoHistoricalDataClient, StockHistoricalDataClient
|
||||
from alpaca.data.requests import CryptoLatestTradeRequest, StockLatestTradeRequest, StockLatestBarRequest, StockTradesRequest
|
||||
from alpaca.data.enums import DataFeed
|
||||
from config import API_KEY, SECRET_KEY, MAX_BATCH_SIZE
|
||||
from datetime import datetime, timezone, time, timedelta, date
|
||||
import pytz
|
||||
from rich import print
|
||||
|
||||
#práce s datumy
|
||||
|
||||
zone_NY = pytz.timezone('America/New_York')
|
||||
|
||||
parametry = {}
|
||||
symbol = ["BAC"]
|
||||
client = StockHistoricalDataClient(API_KEY, SECRET_KEY, raw_data=True)
|
||||
datetime_object_from = datetime(2023, 3, 16, 9, 30, 0, tzinfo=zone_NY)
|
||||
datetime_object_to = datetime(2023, 3, 16, 16, 00, 0, tzinfo=zone_NY)
|
||||
trades_request = StockTradesRequest(symbol_or_symbols=symbol, feed = DataFeed.SIP, start=datetime_object_from, end=datetime_object_to)
|
||||
|
||||
all_trades = client.get_stock_trades(trades_request)
|
||||
|
||||
#print(all_trades)
|
||||
|
||||
print(len(all_trades['BAC']))
|
||||
|
||||
# for i in all_trades:
|
||||
# print(all_trades[i])
|
||||
|
||||
if __name__ == "__main__":
|
||||
# bar will be invoked if this module is being run directly, but not via import!
|
||||
print("hello")
|
||||
49
testy/loghandlers.py
Normal file
49
testy/loghandlers.py
Normal file
@ -0,0 +1,49 @@
|
||||
from v2realbot.enums.enums import Mode, Account
|
||||
from v2realbot.config import get_key
|
||||
import structlog
|
||||
from rich import print
|
||||
from datetime import datetime
|
||||
from v2realbot.utils.utils import zoneNY
|
||||
|
||||
def timestamper(_, __, event_dict):
|
||||
event_dict["time"] = datetime.now().isoformat()
|
||||
return event_dict
|
||||
|
||||
#structlog.configure(processors=[timestamper, structlog.processors.KeyValueRenderer()])
|
||||
|
||||
log = structlog.get_logger()
|
||||
|
||||
def neco(arg: int):
|
||||
log.bind(arg=arg)
|
||||
log.info("neco funkce")
|
||||
arg = arg + 2
|
||||
return arg
|
||||
|
||||
def neco2(kwargs):
|
||||
print("neco 2")
|
||||
for i in 12:
|
||||
print(i)
|
||||
|
||||
ted = datetime.now().astimezone(zoneNY)
|
||||
promena = [1,2]
|
||||
|
||||
log.bind(ted=ted, promena=promena)
|
||||
|
||||
d = dict(a=2,b="33",dalsiklic=4432, pole=[2,3,4])
|
||||
log.info("beforeprint")
|
||||
print(d)
|
||||
d = neco(3)
|
||||
log.info("udalost")
|
||||
|
||||
|
||||
log.info("incoming",d=d)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
88
testy/modulmistoclass-websocket.py
Normal file
88
testy/modulmistoclass-websocket.py
Normal file
@ -0,0 +1,88 @@
|
||||
from threading import Thread, current_thread
|
||||
from alpaca.data.live import StockDataStream, CryptoDataStream
|
||||
from v2realbot.config import API_KEY, SECRET_KEY, MAX_BATCH_SIZE, PAPER
|
||||
import queue
|
||||
from alpaca.data.enums import DataFeed
|
||||
from typing_extensions import Any
|
||||
import time
|
||||
from v2realbot.loader.aggregator import TradeAggregator
|
||||
|
||||
# class ws_agg() :
|
||||
# def __init__(self, client, symbol) -> None:
|
||||
# # Call the Thread class's init function
|
||||
# Thread.__init__(self)
|
||||
# self.client = client
|
||||
# self.symbol = symbol
|
||||
|
||||
|
||||
|
||||
#object composition
|
||||
ws_client = CryptoDataStream(API_KEY, SECRET_KEY, raw_data=True, websocket_params={})
|
||||
_streams = []
|
||||
|
||||
def add_stream(self, **data):
|
||||
#object composition - pomocí append
|
||||
self._streams.append(data)
|
||||
|
||||
async def handler(self, data):
|
||||
print("handler ve threadu:",current_thread().name)
|
||||
# podíváme kolik streamů je instancovaných pro tento symbol - v dict[symbol] a spusteni
|
||||
# pro každý stream zavoláme
|
||||
|
||||
print(data)
|
||||
print("*"*40)
|
||||
|
||||
def run(self) :
|
||||
print(current_thread().name)
|
||||
print(self._streams)
|
||||
unique = set()
|
||||
## for each symbol we subscribe
|
||||
for i in self._streams:
|
||||
print(i['symbol'])
|
||||
#instanciace tradeAggregatoru a uložení do dict[symbol]
|
||||
#zde
|
||||
unique.add(i['symbol'])
|
||||
print(unique)
|
||||
#subscribe for unique symbols
|
||||
|
||||
#
|
||||
##TODO *PROBLEM* co kdyz chci subscribe stejneho symbolu co uz konzumuje jina strategie. PROBLEM koncepční
|
||||
##TODO pri skonceni jedne strategie, udelat teardown kroky jako unsubscribe pripadne stop
|
||||
for i in unique:
|
||||
WS_Stream.client.subscribe_trades(self.handler, i)
|
||||
print("subscribed to",i)
|
||||
#timto se spusti jenom poprve v 1 vlaknu
|
||||
#ostatni pouze vyuzivaji
|
||||
if WS_Stream.client._running is False:
|
||||
print("it is not running, starting by calling RUN")
|
||||
WS_Stream.client.run()
|
||||
#tímto se spustí pouze 1.vlakno, nicmene subscribe i pripadny unsubscribe zafunguji
|
||||
else:
|
||||
print("it is running, not calling RUN")
|
||||
|
||||
|
||||
# class SymbolStream():
|
||||
# def __init__(self, symbol) -> None:
|
||||
# self.symbol = symbol
|
||||
# s
|
||||
# class StreamRequest:
|
||||
# symbol: str
|
||||
# resolution: int
|
||||
|
||||
#clientDataStream = CryptoDataStream(API_KEY, SECRET_KEY, raw_data=True, websocket_params={})
|
||||
|
||||
# novy ws stream - vždy jednom vláknu
|
||||
obj= WS_Stream("jednicka")
|
||||
obj.add_stream(symbol="BTC/USD",resolution=15)
|
||||
# novy ws stream - vždy jednom vláknu
|
||||
obj2= WS_Stream("dvojka")
|
||||
obj2.add_stream(symbol="ETH/USD",resolution=5)
|
||||
obj.start()
|
||||
time.sleep(1)
|
||||
obj2.start()
|
||||
# clientDataStream.run()
|
||||
# clientDataStream2.run()
|
||||
obj2.join()
|
||||
obj.join()
|
||||
|
||||
print("po startu")
|
||||
75
testy/pandasinsert.py
Normal file
75
testy/pandasinsert.py
Normal file
@ -0,0 +1,75 @@
|
||||
from uuid import UUID, uuid4
|
||||
from alpaca.trading.enums import OrderSide, OrderStatus, TradeEvent, OrderType
|
||||
from common.model import TradeUpdate, Order
|
||||
from rich import print
|
||||
import threading
|
||||
import asyncio
|
||||
from config import BT_DELAYS
|
||||
from utils.utils import AttributeDict, ltp, zoneNY, trunc
|
||||
from utils.tlog import tlog
|
||||
from datetime import datetime
|
||||
import pandas as pd
|
||||
import mplfinance as mpf
|
||||
|
||||
|
||||
trade1 = TradeUpdate(order =Order(id=uuid4(),
|
||||
submitted_at = datetime(2023, 3, 17, 9, 30, 0, 0, tzinfo=zoneNY),
|
||||
symbol = "BAC",
|
||||
qty = 1,
|
||||
status = OrderStatus.ACCEPTED,
|
||||
order_type = OrderType.LIMIT,
|
||||
side = OrderSide.BUY,
|
||||
limit_price=22.4),
|
||||
event = TradeEvent.FILL,
|
||||
execution_id = uuid4(),
|
||||
timestamp = datetime.now(),
|
||||
position_qty= 2,
|
||||
price=22.3,
|
||||
qty = 2,
|
||||
value = 44.6)
|
||||
|
||||
trade2 = TradeUpdate(order =Order(id=uuid4(),
|
||||
submitted_at = datetime(2023, 3, 17, 9, 34, 0, 0, tzinfo=zoneNY),
|
||||
symbol = "BAC",
|
||||
qty = 1,
|
||||
status = OrderStatus.ACCEPTED,
|
||||
order_type = OrderType.LIMIT,
|
||||
side = OrderSide.SELL,
|
||||
limit_price=22.4),
|
||||
event = TradeEvent.FILL,
|
||||
execution_id = uuid4(),
|
||||
timestamp = datetime.now(),
|
||||
position_qty= 2,
|
||||
price=24.3,
|
||||
qty = 2,
|
||||
value = 48.6)
|
||||
trades= [trade1,trade2]
|
||||
#print(trades)
|
||||
trade_dict = AttributeDict(timestamp=[],symbol=[],qty=[],price=[],position_qty=[],value=[])
|
||||
|
||||
for t in trades:
|
||||
trade_dict.timestamp.append(t.timestamp)
|
||||
trade_dict.symbol.append(t.order.symbol)
|
||||
trade_dict.qty.append(t.qty)
|
||||
trade_dict.price.append(t.price)
|
||||
trade_dict.position_qty.append(t.position_qty)
|
||||
trade_dict.value.append(t.value)
|
||||
|
||||
print(trade_dict)
|
||||
|
||||
trade_df = pd.DataFrame(trade_dict)
|
||||
trade_df = trade_df.set_index('timestamp')
|
||||
|
||||
mpf.plot(trade_df, # the dataframe containing the OHLC (Open, High, Low and Close) data
|
||||
type='candle', # use candlesticks
|
||||
volume=True, # also show the volume
|
||||
mav=(3,6,9), # use three different moving averages
|
||||
figratio=(3,1), # set the ratio of the figure
|
||||
style='yahoo', # choose the yahoo style
|
||||
title='Bitcoin on Wednesday morning');
|
||||
|
||||
print(trade_df)
|
||||
#pd.DataFrame()
|
||||
|
||||
|
||||
#self.trades.append(trade)
|
||||
133
testy/pracesdatumem.py
Normal file
133
testy/pracesdatumem.py
Normal file
@ -0,0 +1,133 @@
|
||||
from alpaca.data.historical import CryptoHistoricalDataClient, StockHistoricalDataClient
|
||||
from alpaca.data.requests import CryptoLatestTradeRequest, StockLatestTradeRequest, StockLatestBarRequest, StockTradesRequest
|
||||
from alpaca.data.enums import DataFeed
|
||||
from v2realbot.config import API_KEY, SECRET_KEY, MAX_BATCH_SIZE
|
||||
#from v2realbot.utils.utils import zoneNY
|
||||
from datetime import datetime, timezone, time, timedelta, date
|
||||
import pytz
|
||||
from rich import print
|
||||
from pandas import to_datetime
|
||||
|
||||
#práce s datumy
|
||||
|
||||
zone_NY = pytz.timezone('America/New_York')
|
||||
zoneNY=zone_NY
|
||||
|
||||
# parametry = {}
|
||||
# symbol = ["C","BAC"]
|
||||
# client = StockHistoricalDataClient(API_KEY, SECRET_KEY, raw_data=True)
|
||||
# datetime_object_from = datetime(2023, 3, 16, 17, 51, 38, tzinfo=timezone.utc)
|
||||
# datetime_object_to = datetime(2023, 3, 16, 17, 52, 39, tzinfo=timezone.utc)
|
||||
# trades_request = StockTradesRequest(symbol_or_symbols=symbol, feed = DataFeed.SIP, start=datetime_object_from, end=datetime_object_to)
|
||||
|
||||
# all_trades = client.get_stock_trades(trades_request)
|
||||
|
||||
# print(len(all_trades))
|
||||
|
||||
# for i in all_trades:
|
||||
# print(all_trades[i])
|
||||
|
||||
# timeZ_Ny = pytz.timezone('America/New_York')
|
||||
# MARKET_OPEN = time(hour=9, minute=30, second=0, tzinfo=timeZ_Ny)
|
||||
# MARKET_CLOSE = time(hour=16, minute=30, second=0, tzinfo=timeZ_Ny)
|
||||
|
||||
# print(MARKET_OPEN)
|
||||
# print(MARKET_CLOSE)
|
||||
|
||||
|
||||
|
||||
def is_open_rush(dt: datetime, mins: int = 30):
|
||||
""""
|
||||
Returns true if time is within morning rush (open+mins)
|
||||
"""
|
||||
dt = dt.astimezone(zoneNY)
|
||||
business_hours = {
|
||||
"from": time(hour=9, minute=30),
|
||||
"to": time(hour=16, minute=0)
|
||||
}
|
||||
rushtime = (datetime.combine(date.today(), business_hours["from"]) + timedelta(minutes=mins)).time()
|
||||
return business_hours["from"] <= dt.time() < rushtime
|
||||
|
||||
def is_close_rush(dt: datetime, mins: int = 30):
|
||||
""""
|
||||
Returns true if time is within morning rush (open+mins)
|
||||
"""
|
||||
dt = dt.astimezone(zoneNY)
|
||||
business_hours = {
|
||||
"from": time(hour=9, minute=30),
|
||||
"to": time(hour=16, minute=0)
|
||||
}
|
||||
rushtime = (datetime.combine(date.today(), business_hours["to"]) - timedelta(minutes=mins)).time()
|
||||
return rushtime <= dt.time() <= business_hours["to"]
|
||||
|
||||
|
||||
now = datetime.now(tz=zone_NY)
|
||||
now = datetime(2023, 3, 16, 15, 50, 00, tzinfo=zone_NY)
|
||||
print(now)
|
||||
print("is closing rush", is_close_rush(now, 0))
|
||||
|
||||
|
||||
|
||||
""""
|
||||
TODO toto pridat do utils a pak bud do agregatoru
|
||||
a nebo do spis offline_loaderu (tam je muzu filtrovat) - pripadne nejake flagy
|
||||
pak pokracovat v BASE kde jsem skoncil vcera
|
||||
|
||||
|
||||
returns if date is within market open times (no holidays implemented yet)
|
||||
input is timezone aware datetime
|
||||
"""
|
||||
def is_open_hours(dt):
|
||||
|
||||
dt = dt.astimezone(pytz.timezone('America/New_York'))
|
||||
print("ameriko time", dt)
|
||||
|
||||
business_hours = {
|
||||
# monday = 0, tuesday = 1, ... same pattern as date.weekday()
|
||||
"weekdays": [0, 1, 2, 3, 4],
|
||||
"from": time(hour=9, minute=30),
|
||||
"to": time(hour=16, minute=30)
|
||||
}
|
||||
|
||||
holidays = [date(2022, 12, 24), date(2022, 2, 24)]
|
||||
|
||||
return dt.weekday() in business_hours["weekdays"] \
|
||||
and dt.date() not in holidays \
|
||||
and business_hours["from"] <= dt.time() < business_hours["to"]
|
||||
|
||||
now = datetime.now(tz=zone_NY)
|
||||
now = datetime(2023, 3, 16, 15, 51, 38, tzinfo=zone_NY)
|
||||
now = datetime(2023, 3, 16, 15, 51, 38, tzinfo=timezone.utc)
|
||||
print(now)
|
||||
print("is business hour", is_open_hours(now))
|
||||
|
||||
|
||||
def parse_nanodate(s):
|
||||
"""
|
||||
parse date, ignore nanoseconds
|
||||
sample input: 2020-12-31T16:20:00.000000123Z
|
||||
--> 123ns will be ignored
|
||||
"""
|
||||
print(s[0:26]+s[len(s) - 1]+'+0000')
|
||||
return datetime.strptime(
|
||||
s[0:26]+s[len(s) - 1]+'+0000', '%Y-%m-%dT%H:%M:%S.%fZ%z')
|
||||
|
||||
a = "2023-03-17T12:56:37.588388864Z"
|
||||
b = "2023-03-17T12:56:41.332702720Z"
|
||||
c = "2023-03-17T12:56:41.3327027Z"
|
||||
d = "2023-03-17T12:01:36.13168Z"
|
||||
|
||||
|
||||
print(to_datetime(d))
|
||||
|
||||
print(int(datetime(2023, 3, 17, 9, 30, 0, 0, tzinfo=zone_NY).timestamp()))
|
||||
print(int(datetime(2023, 3, 17, 16, 00, 0, 0, tzinfo=zone_NY).timestamp()))
|
||||
|
||||
# print(a)
|
||||
# print(parse_nanodate(a).astimezone(tz=zone_NY))
|
||||
# print(b)
|
||||
# print(parse_nanodate(b))
|
||||
# print(c)
|
||||
# print(parse_nanodate(c))
|
||||
# print(d)
|
||||
# print(parse_nanodate(d))
|
||||
19
testy/printoverride.py
Normal file
19
testy/printoverride.py
Normal file
@ -0,0 +1,19 @@
|
||||
from rich import print
|
||||
from icecream import ic
|
||||
|
||||
|
||||
|
||||
def p(*args, **kwargs):
|
||||
if ic.enabled:
|
||||
print(*args, **kwargs)
|
||||
else:
|
||||
|
||||
|
||||
|
||||
p("nazdar")
|
||||
a = "helo"
|
||||
b = dict(a=123,b="CUS")
|
||||
c = 123
|
||||
|
||||
p(a,b,c,"nazdar")
|
||||
p("nazdar","covece",a,c)
|
||||
233
testy/testExecList-bisect.py
Normal file
233
testy/testExecList-bisect.py
Normal file
@ -0,0 +1,233 @@
|
||||
"""
|
||||
Zjistovani ceny z listu tradu pomocí bisect left
|
||||
"""
|
||||
from datetime import datetime
|
||||
from bisect import bisect_left
|
||||
|
||||
btdata = [(1679081913.290388, 27.8634), (1679081913.68588, 27.865), (1679081913.986394, 27.86), (1679081914.095521, 27.865), (1679081914.396844, 27.8601), (1679081914.601457, 27.865), (1679081914.721968, 27.86), (1679081914.739287, 27.86), (1679081914.739305, 27.865), (1679081914.739314, 27.865), (1679081914.73941, 27.865), (1679081914.739554, 27.86), (1679081914.739569, 27.86), (1679081914.739572, 27.86), (1679081914.739635, 27.86), (1679081914.739644, 27.86), (1679081914.739771, 27.86), (1679081914.74, 27.865), (1679081914.74048, 27.865), (1679081914.740531, 27.865), (1679081914.740691, 27.865), (1679081914.746943, 27.865), (1679081914.779766, 27.86), (1679081914.779769, 27.86), (1679081914.779901, 27.86), (1679081914.779904, 27.865), (1679081914.77991, 27.865), (1679081914.780006, 27.865), (1679081914.780388, 27.865), (1679081914.780415, 27.865), (1679081914.79638, 27.86), (1679081914.79638, 27.86), (1679081914.796383, 27.865), (1679081914.796498, 27.865), (1679081914.796901, 27.865), (1679081914.816074, 27.865), (1679081914.942793, 27.865), (1679081915.424626, 27.8625), (1679081915.863117, 27.865), (1679081915.863255, 27.8675), (1679081915.870084, 27.865), (1679081915.877677, 27.865), (1679081916.015251, 27.865), (1679081916.018716, 27.865), (1679081916.494838, 27.8656), (1679081916.827929, 27.868), (1679081916.870675, 27.8636), (1679081917.140228, 27.87), (1679081917.140763, 27.87), (1679081917.150359, 27.865), (1679081917.753467, 27.865), (1679081917.853001, 27.865), (1679081918.012672, 27.865), (1679081918.736837, 27.865), (1679081918.737011, 27.865), (1679081918.737177, 27.87), (1679081918.742472, 27.87), (1679081918.743335, 27.87), (1679081918.868673, 27.8699), (1679081919.01883, 27.87), (1679081919.018832, 27.87), (1679081919.018835, 27.87), (1679081919.018839, 27.87), (1679081919.018839, 27.87), (1679081919.018857, 27.87), (1679081919.018905, 27.87), (1679081919.018911, 27.87), (1679081919.018911, 27.87), (1679081919.018914, 27.87), (1679081919.018914, 27.87), (1679081919.01892, 27.87), (1679081919.01892, 27.87), (1679081919.018923, 27.87), (1679081919.018929, 27.87), (1679081919.018932, 27.87), (1679081919.018938, 27.87), (1679081919.018941, 27.87), (1679081919.018947, 27.87), (1679081919.01895, 27.87), (1679081919.018956, 27.87), (1679081919.018968, 27.87), (1679081919.018986, 27.87), (1679081919.019074, 27.87), (1679081919.019077, 27.87), (1679081919.019077, 27.87), (1679081919.019079, 27.87), (1679081919.019082, 27.87), (1679081919.019082, 27.87), (1679081919.019095, 27.87), (1679081919.019095, 27.87), (1679081919.0191, 27.87), (1679081919.019103, 27.87), (1679081919.019106, 27.87), (1679081919.019109, 27.87), (1679081919.019112, 27.87), (1679081919.019112, 27.87), (1679081919.019124, 27.87), (1679081919.019127, 27.87), (1679081919.019133, 27.87), (1679081919.019139, 27.87), (1679081919.019323, 27.87), (1679081919.019323, 27.87), (1679081919.019323, 27.87), (1679081919.019323, 27.87), (1679081919.019326, 27.87), (1679081919.019326, 27.87), (1679081919.019936, 27.87), (1679081919.019978, 27.87), (1679081919.020189, 27.87), (1679081919.020264, 27.87), (1679081919.020312, 27.87), (1679081919.020628, 27.87), (1679081919.025445, 27.87), (1679081919.02565, 27.87), (1679081919.066583, 27.87), (1679081919.066953, 27.87), (1679081919.067248, 27.87), (1679081919.067398, 27.875), (1679081919.067672, 27.875), (1679081919.067939, 27.875), (1679081919.067975, 27.875), (1679081919.071849, 27.875), (1679081919.157709, 27.875), (1679081919.184806, 27.875), (1679081919.301574, 27.87), (1679081919.381201, 27.88), (1679081919.381204, 27.88), (1679081919.381237, 27.88), (1679081919.381264, 27.875), (1679081919.381643, 27.88), (1679081919.381649, 27.88), (1679081919.381676, 27.88), (1679081919.381685, 27.88), (1679081919.381697, 27.88), (1679081919.381706, 27.88), (1679081919.381718, 27.88), (1679081919.395142, 27.875), (1679081919.469476, 27.88), (1679081919.570886, 27.88), (1679081919.690577, 27.875), (1679081920.168907, 27.878)]
|
||||
|
||||
|
||||
###
|
||||
# 1679081919.381264
|
||||
# 1679081919.381643
|
||||
# 1679081919.381649
|
||||
|
||||
#orizneme pole
|
||||
|
||||
|
||||
index_start = None
|
||||
index_end = None
|
||||
range_start = 1679081914.73941
|
||||
range_end = 1679081917.150359
|
||||
print("range_start",range_start)
|
||||
print("range_end",range_end)
|
||||
|
||||
a= datetime.now().timestamp()
|
||||
print("start 1.varianta", a)
|
||||
# for i in range(len(btdata)):
|
||||
# print(btdata[i][0])
|
||||
# print(i)
|
||||
# if btdata[i][0] <= range_start: index_start = i
|
||||
# if btdata[i][0] >= range_end:
|
||||
# index_end = i
|
||||
# break
|
||||
|
||||
print("index_start", index_start)
|
||||
print("index_end", index_end)
|
||||
print("oriznuto",btdata[index_start:index_end+1])
|
||||
|
||||
new_range =btdata[index_start:index_end+1]
|
||||
|
||||
#LIMIT FILL - BUY
|
||||
submitted_at: float = 1679081914.739644
|
||||
limit_price: float = 27.865
|
||||
fill_time = None
|
||||
bisect_left(submitted_at + 0.020,)
|
||||
|
||||
|
||||
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 - SELL
|
||||
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 SELL at", fill_time, "at",i[1])
|
||||
break
|
||||
if not fill_time: print("NO FILL for ", limit_price)
|
||||
|
||||
|
||||
#MARKET FILL BUY/SELL:
|
||||
for i in new_range:
|
||||
#print(i)
|
||||
#najde prvni nejvetsi čas vetsi nez minfill
|
||||
if i[0] > submitted_at + 0.020:
|
||||
#(1679081919.381649, 27.88)
|
||||
print(i)
|
||||
print("FILL MARKET at", i[0], "cena", i[1])
|
||||
break
|
||||
|
||||
b= datetime.now().timestamp()
|
||||
print("stop 1.varianta", b)
|
||||
print("rozdil", b-a)
|
||||
#0.0006699562072753906
|
||||
#0.0007920265197753906
|
||||
|
||||
|
||||
# (1679081913.290388, 27.8634)
|
||||
# (1679081913.68588, 27.865)
|
||||
# (1679081913.986394, 27.86)
|
||||
# (1679081914.095521, 27.865)
|
||||
# (1679081914.396844, 27.8601)
|
||||
# (1679081914.601457, 27.865)
|
||||
# (1679081914.721968, 27.86)
|
||||
# (1679081914.739287, 27.86)
|
||||
# (1679081914.739305, 27.865)
|
||||
# (1679081914.739314, 27.865)
|
||||
# (1679081914.73941, 27.865)*
|
||||
# (1679081914.739554, 27.86)
|
||||
# (1679081914.739569, 27.86)
|
||||
# (1679081914.739572, 27.86)
|
||||
# (1679081914.739635, 27.86)
|
||||
# (1679081914.739644, 27.86)submit
|
||||
# (1679081914.739771, 27.86)
|
||||
# (1679081914.74, 27.865)
|
||||
# (1679081914.74048, 27.865)
|
||||
# (1679081914.740531, 27.865)
|
||||
# (1679081914.740691, 27.865)
|
||||
# (1679081914.746943, 27.865)
|
||||
# (1679081914.779766, 27.86)
|
||||
# (1679081914.779769, 27.86)
|
||||
# (1679081914.779901, 27.86)
|
||||
# (1679081914.779904, 27.865)
|
||||
# (1679081914.77991, 27.865)
|
||||
# (1679081914.780006, 27.865)
|
||||
# (1679081914.780388, 27.865)
|
||||
# (1679081914.780415, 27.865)
|
||||
# (1679081914.79638, 27.86)
|
||||
# (1679081914.79638, 27.86)
|
||||
# (1679081914.796383, 27.865)
|
||||
# (1679081914.796498, 27.865)
|
||||
# (1679081914.796901, 27.865)
|
||||
# (1679081914.816074, 27.865)
|
||||
# (1679081914.942793, 27.865)
|
||||
# (1679081915.424626, 27.8625)
|
||||
# (1679081915.863117, 27.865)
|
||||
# (1679081915.863255, 27.8675)
|
||||
# (1679081915.870084, 27.865)
|
||||
# (1679081915.877677, 27.865)
|
||||
# (1679081916.015251, 27.865)
|
||||
# (1679081916.018716, 27.865)
|
||||
# (1679081916.494838, 27.8656)
|
||||
# (1679081916.827929, 27.868)
|
||||
# (1679081916.870675, 27.8636)
|
||||
# (1679081917.140228, 27.87)
|
||||
# (1679081917.140763, 27.87)
|
||||
# (1679081917.150359, 27.865)end
|
||||
# (1679081917.753467, 27.865)
|
||||
# (1679081917.853001, 27.865)
|
||||
# (1679081918.012672, 27.865)
|
||||
# (1679081918.736837, 27.865)
|
||||
# (1679081918.737011, 27.865)
|
||||
# (1679081918.737177, 27.87)
|
||||
# (1679081918.742472, 27.87)
|
||||
# (1679081918.743335, 27.87)
|
||||
# (1679081918.868673, 27.8699)
|
||||
# (1679081919.01883, 27.87)
|
||||
# (1679081919.018832, 27.87)
|
||||
# (1679081919.018835, 27.87)
|
||||
# (1679081919.018839, 27.87)
|
||||
# (1679081919.018839, 27.87)
|
||||
# (1679081919.018857, 27.87)
|
||||
# (1679081919.018905, 27.87)
|
||||
# (1679081919.018911, 27.87)
|
||||
# (1679081919.018911, 27.87)
|
||||
# (1679081919.018914, 27.87)
|
||||
# (1679081919.018914, 27.87)
|
||||
# (1679081919.01892, 27.87)
|
||||
# (1679081919.01892, 27.87)
|
||||
# (1679081919.018923, 27.87)
|
||||
# (1679081919.018929, 27.87)
|
||||
# (1679081919.018932, 27.87)
|
||||
# (1679081919.018938, 27.87)
|
||||
# (1679081919.018941, 27.87)
|
||||
# (1679081919.018947, 27.87)
|
||||
# (1679081919.01895, 27.87)
|
||||
# (1679081919.018956, 27.87)
|
||||
# (1679081919.018968, 27.87)
|
||||
# (1679081919.018986, 27.87)
|
||||
# (1679081919.019074, 27.87)
|
||||
# (1679081919.019077, 27.87)
|
||||
# (1679081919.019077, 27.87)
|
||||
# (1679081919.019079, 27.87)
|
||||
# (1679081919.019082, 27.87)
|
||||
# (1679081919.019082, 27.87)
|
||||
# (1679081919.019095, 27.87)
|
||||
# (1679081919.019095, 27.87)
|
||||
# (1679081919.0191, 27.87)
|
||||
# (1679081919.019103, 27.87)
|
||||
# (1679081919.019106, 27.87)
|
||||
# (1679081919.019109, 27.87)
|
||||
# (1679081919.019112, 27.87)
|
||||
# (1679081919.019112, 27.87)
|
||||
# (1679081919.019124, 27.87)
|
||||
# (1679081919.019127, 27.87)
|
||||
# (1679081919.019133, 27.87)
|
||||
# (1679081919.019139, 27.87)
|
||||
# (1679081919.019323, 27.87)
|
||||
# (1679081919.019323, 27.87)
|
||||
# (1679081919.019323, 27.87)
|
||||
# (1679081919.019323, 27.87)
|
||||
# (1679081919.019326, 27.87)
|
||||
# (1679081919.019326, 27.87)
|
||||
# (1679081919.019936, 27.87)
|
||||
# (1679081919.019978, 27.87)
|
||||
# (1679081919.020189, 27.87)
|
||||
# (1679081919.020264, 27.87)
|
||||
# (1679081919.020312, 27.87)
|
||||
# (1679081919.020628, 27.87)
|
||||
# (1679081919.025445, 27.87)
|
||||
# (1679081919.02565, 27.87)
|
||||
# (1679081919.066583, 27.87)
|
||||
# (1679081919.066953, 27.87)
|
||||
# (1679081919.067248, 27.87)
|
||||
# (1679081919.067398, 27.875)
|
||||
# (1679081919.067672, 27.875)
|
||||
# (1679081919.067939, 27.875)
|
||||
# (1679081919.067975, 27.875)
|
||||
# (1679081919.071849, 27.875)
|
||||
# (1679081919.157709, 27.875)
|
||||
# (1679081919.184806, 27.875)
|
||||
# (1679081919.301574, 27.87)
|
||||
# (1679081919.381201, 27.88)
|
||||
# (1679081919.381204, 27.88)
|
||||
# (1679081919.381237, 27.88)
|
||||
# (1679081919.381264, 27.875)
|
||||
# (1679081919.381643, 27.88)
|
||||
# (1679081919.381649, 27.88)
|
||||
# (1679081919.381676, 27.88)
|
||||
# (1679081919.381685, 27.88)
|
||||
# (1679081919.381697, 27.88)
|
||||
# (1679081919.381706, 27.88)
|
||||
# (1679081919.381718, 27.88)
|
||||
# (1679081919.395142, 27.875)
|
||||
# (1679081919.469476, 27.88)
|
||||
# (1679081919.570886, 27.88)
|
||||
# (1679081919.690577, 27.875)
|
||||
# (1679081920.168907, 27.878)
|
||||
354
testy/testExecList-standard.py
Normal file
354
testy/testExecList-standard.py
Normal file
@ -0,0 +1,354 @@
|
||||
"""
|
||||
standardni pristup zjistovani ceny z listu tradu
|
||||
"""
|
||||
|
||||
from datetime import datetime
|
||||
import timeit
|
||||
|
||||
|
||||
btdata = [(1679081913.290388, 27.8634), (1679081913.68588, 27.865), (1679081913.986394, 27.86), (1679081914.095521, 27.865), (1679081914.396844, 27.8601), (1679081914.601457, 27.865), (1679081914.721968, 27.86), (1679081914.739287, 27.86), (1679081914.739305, 27.865), (1679081914.739314, 27.865), (1679081914.73941, 27.865), (1679081914.739554, 27.86), (1679081914.739569, 27.86), (1679081914.739572, 27.86), (1679081914.739635, 27.86), (1679081914.739644, 27.86), (1679081914.739771, 27.86), (1679081914.74, 27.865), (1679081914.74048, 27.865), (1679081914.740531, 27.865), (1679081914.740691, 27.865), (1679081914.746943, 27.865), (1679081914.779766, 27.86), (1679081914.779769, 27.86), (1679081914.779901, 27.86), (1679081914.779904, 27.865), (1679081914.77991, 27.865), (1679081914.780006, 27.865), (1679081914.780388, 27.865), (1679081914.780415, 27.865), (1679081914.79638, 27.86), (1679081914.79638, 27.86), (1679081914.796383, 27.865), (1679081914.796498, 27.865), (1679081914.796901, 27.865), (1679081914.816074, 27.865), (1679081914.942793, 27.865), (1679081915.424626, 27.8625), (1679081915.863117, 27.865), (1679081915.863255, 27.8675), (1679081915.870084, 27.865), (1679081915.877677, 27.865), (1679081916.015251, 27.865), (1679081916.018716, 27.865), (1679081916.494838, 27.8656), (1679081916.827929, 27.868), (1679081916.870675, 27.8636), (1679081917.140228, 27.87), (1679081917.140763, 27.87), (1679081917.150359, 27.865), (1679081917.753467, 27.865), (1679081917.853001, 27.865), (1679081918.012672, 27.865), (1679081918.736837, 27.865), (1679081918.737011, 27.865), (1679081918.737177, 27.87), (1679081918.742472, 27.87), (1679081918.743335, 27.87), (1679081918.868673, 27.8699), (1679081919.01883, 27.87), (1679081919.018832, 27.87), (1679081919.018835, 27.87), (1679081919.018839, 27.87), (1679081919.018839, 27.87), (1679081919.018857, 27.87), (1679081919.018905, 27.87), (1679081919.018911, 27.87), (1679081919.018911, 27.87), (1679081919.018914, 27.87), (1679081919.018914, 27.87), (1679081919.01892, 27.87), (1679081919.01892, 27.87), (1679081919.018923, 27.87), (1679081919.018929, 27.87), (1679081919.018932, 27.87), (1679081919.018938, 27.87), (1679081919.018941, 27.87), (1679081919.018947, 27.87), (1679081919.01895, 27.87), (1679081919.018956, 27.87), (1679081919.018968, 27.87), (1679081919.018986, 27.87), (1679081919.019074, 27.87), (1679081919.019077, 27.87), (1679081919.019077, 27.87), (1679081919.019079, 27.87), (1679081919.019082, 27.87), (1679081919.019082, 27.87), (1679081919.019095, 27.87), (1679081919.019095, 27.87), (1679081919.0191, 27.87), (1679081919.019103, 27.87), (1679081919.019106, 27.87), (1679081919.019109, 27.87), (1679081919.019112, 27.87), (1679081919.019112, 27.87), (1679081919.019124, 27.87), (1679081919.019127, 27.87), (1679081919.019133, 27.87), (1679081919.019139, 27.87), (1679081919.019323, 27.87), (1679081919.019323, 27.87), (1679081919.019323, 27.87), (1679081919.019323, 27.87), (1679081919.019326, 27.87), (1679081919.019326, 27.87), (1679081919.019936, 27.87), (1679081919.019978, 27.87), (1679081919.020189, 27.87), (1679081919.020264, 27.87), (1679081919.020312, 27.87), (1679081919.020628, 27.87), (1679081919.025445, 27.87), (1679081919.02565, 27.87), (1679081919.066583, 27.87), (1679081919.066953, 27.87), (1679081919.067248, 27.87), (1679081919.067398, 27.875), (1679081919.067672, 27.875), (1679081919.067939, 27.875), (1679081919.067975, 27.875), (1679081919.071849, 27.875), (1679081919.157709, 27.875), (1679081919.184806, 27.875), (1679081919.301574, 27.87), (1679081919.381201, 27.88), (1679081919.381204, 27.88), (1679081919.381237, 27.88), (1679081919.381264, 27.875), (1679081919.381643, 27.88), (1679081919.381649, 27.88), (1679081919.381676, 27.88), (1679081919.381685, 27.88), (1679081919.381697, 27.88), (1679081919.381706, 27.88), (1679081919.381718, 27.88), (1679081919.395142, 27.875), (1679081919.469476, 27.88), (1679081919.570886, 27.88), (1679081919.690577, 27.875), (1679081920.168907, 27.878)]
|
||||
|
||||
|
||||
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 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))
|
||||
|
||||
#(1679081919.018929, 27.87), (1679081919.018932, 27.87), (1679081919.018938, 27.87),
|
||||
|
||||
# def looper(cislo, btdata):
|
||||
# for i in range(len(btdata)):
|
||||
# #print(btdata[i][0])
|
||||
# #print(i)
|
||||
# if btdata[i][0] >= cislo:
|
||||
# index_end = i
|
||||
# break
|
||||
# return btdata[i]
|
||||
|
||||
# setup = '''
|
||||
|
||||
# btdata = [(1679081913.290388, 27.8634), (1679081913.68588, 27.865), (1679081913.986394, 27.86), (1679081914.095521, 27.865), (1679081914.396844, 27.8601), (1679081914.601457, 27.865), (1679081914.721968, 27.86), (1679081914.739287, 27.86), (1679081914.739305, 27.865), (1679081914.739314, 27.865), (1679081914.73941, 27.865), (1679081914.739554, 27.86), (1679081914.739569, 27.86), (1679081914.739572, 27.86), (1679081914.739635, 27.86), (1679081914.739644, 27.86), (1679081914.739771, 27.86), (1679081914.74, 27.865), (1679081914.74048, 27.865), (1679081914.740531, 27.865), (1679081914.740691, 27.865), (1679081914.746943, 27.865), (1679081914.779766, 27.86), (1679081914.779769, 27.86), (1679081914.779901, 27.86), (1679081914.779904, 27.865), (1679081914.77991, 27.865), (1679081914.780006, 27.865), (1679081914.780388, 27.865), (1679081914.780415, 27.865), (1679081914.79638, 27.86), (1679081914.79638, 27.86), (1679081914.796383, 27.865), (1679081914.796498, 27.865), (1679081914.796901, 27.865), (1679081914.816074, 27.865), (1679081914.942793, 27.865), (1679081915.424626, 27.8625), (1679081915.863117, 27.865), (1679081915.863255, 27.8675), (1679081915.870084, 27.865), (1679081915.877677, 27.865), (1679081916.015251, 27.865), (1679081916.018716, 27.865), (1679081916.494838, 27.8656), (1679081916.827929, 27.868), (1679081916.870675, 27.8636), (1679081917.140228, 27.87), (1679081917.140763, 27.87), (1679081917.150359, 27.865), (1679081917.753467, 27.865), (1679081917.853001, 27.865), (1679081918.012672, 27.865), (1679081918.736837, 27.865), (1679081918.737011, 27.865), (1679081918.737177, 27.87), (1679081918.742472, 27.87), (1679081918.743335, 27.87), (1679081918.868673, 27.8699), (1679081919.01883, 27.87), (1679081919.018832, 27.87), (1679081919.018835, 27.87), (1679081919.018839, 27.87), (1679081919.018839, 27.87), (1679081919.018857, 27.87), (1679081919.018905, 27.87), (1679081919.018911, 27.87), (1679081919.018911, 27.87), (1679081919.018914, 27.87), (1679081919.018914, 27.87), (1679081919.01892, 27.87), (1679081919.01892, 27.87), (1679081919.018923, 27.87), (1679081919.018929, 27.87), (1679081919.018932, 27.87), (1679081919.018938, 27.87), (1679081919.018941, 27.87), (1679081919.018947, 27.87), (1679081919.01895, 27.87), (1679081919.018956, 27.87), (1679081919.018968, 27.87), (1679081919.018986, 27.87), (1679081919.019074, 27.87), (1679081919.019077, 27.87), (1679081919.019077, 27.87), (1679081919.019079, 27.87), (1679081919.019082, 27.87), (1679081919.019082, 27.87), (1679081919.019095, 27.87), (1679081919.019095, 27.87), (1679081919.0191, 27.87), (1679081919.019103, 27.87), (1679081919.019106, 27.87), (1679081919.019109, 27.87), (1679081919.019112, 27.87), (1679081919.019112, 27.87), (1679081919.019124, 27.87), (1679081919.019127, 27.87), (1679081919.019133, 27.87), (1679081919.019139, 27.87), (1679081919.019323, 27.87), (1679081919.019323, 27.87), (1679081919.019323, 27.87), (1679081919.019323, 27.87), (1679081919.019326, 27.87), (1679081919.019326, 27.87), (1679081919.019936, 27.87), (1679081919.019978, 27.87), (1679081919.020189, 27.87), (1679081919.020264, 27.87), (1679081919.020312, 27.87), (1679081919.020628, 27.87), (1679081919.025445, 27.87), (1679081919.02565, 27.87), (1679081919.066583, 27.87), (1679081919.066953, 27.87), (1679081919.067248, 27.87), (1679081919.067398, 27.875), (1679081919.067672, 27.875), (1679081919.067939, 27.875), (1679081919.067975, 27.875), (1679081919.071849, 27.875), (1679081919.157709, 27.875), (1679081919.184806, 27.875), (1679081919.301574, 27.87), (1679081919.381201, 27.88), (1679081919.381204, 27.88), (1679081919.381237, 27.88), (1679081919.381264, 27.875), (1679081919.381643, 27.88), (1679081919.381649, 27.88), (1679081919.381676, 27.88), (1679081919.381685, 27.88), (1679081919.381697, 27.88), (1679081919.381706, 27.88), (1679081919.381718, 27.88), (1679081919.395142, 27.875), (1679081919.469476, 27.88), (1679081919.570886, 27.88), (1679081919.690577, 27.875), (1679081920.168907, 27.878)]
|
||||
|
||||
|
||||
# from bisect import bisect_left
|
||||
|
||||
# def take_closest(myList, myNumber):
|
||||
# """
|
||||
# Assumes myList is sorted. Returns closest value to myNumber.
|
||||
|
||||
# If two numbers are equally close, return the smallest number.
|
||||
# """
|
||||
# pos = bisect_left(myList, (myNumber,))
|
||||
# if pos == 0:
|
||||
# return myList[0]
|
||||
# if pos == len(myList):
|
||||
# return myList[-1]
|
||||
# after, afterPrice = myList[pos]
|
||||
# return after,afterPrice
|
||||
# # before, beforePrice = myList[pos - 1]
|
||||
# # if after - myNumber < myNumber - before:
|
||||
# # return after,afterPrice
|
||||
# # else:
|
||||
# # return before,beforePrice
|
||||
|
||||
# #(1679081919.018929, 27.87), (1679081919.018932, 27.87), (1679081919.018938, 27.87),
|
||||
|
||||
# def looper(cislo, btdata):
|
||||
# for i in range(len(btdata)):
|
||||
# #print(btdata[i][0])
|
||||
# #print(i)
|
||||
# if btdata[i][0] >= cislo:
|
||||
# index_end = i
|
||||
# break
|
||||
# return btdata[i]
|
||||
|
||||
# '''
|
||||
|
||||
|
||||
# print("bisect")
|
||||
# print(take_closest(btdata, 1679081919.018939))
|
||||
# print("standard")
|
||||
# print(looper(1679081919.018939, btdata))
|
||||
|
||||
# print(timeit.timeit('take_closest(btdata, 1679081919.018939)', setup=setup))
|
||||
# #0.4
|
||||
# print(timeit.timeit('looper(1679081919.018939, btdata)', setup=setup))
|
||||
# #2.4
|
||||
|
||||
|
||||
# ###
|
||||
# # 1679081919.381264
|
||||
# # 1679081919.381643
|
||||
# # 1679081919.381649
|
||||
|
||||
# #orizneme pole
|
||||
|
||||
# """
|
||||
# btdata obsahuje vsechny aktualni timestampy tradu a jejich cenu.
|
||||
# 1) pracujeme vzdy na zacatku listu do indexu odpovidajici aktualnimu casu
|
||||
# 2) zjistime si index a pak iterujeme nad nim
|
||||
# 3) po skonceni pak tento pracovni kus umazeme
|
||||
# """
|
||||
# # def match(time: float):
|
||||
|
||||
# a= datetime.now().timestamp()
|
||||
# print("start 1.varianta", a)
|
||||
|
||||
|
||||
|
||||
# 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)
|
||||
|
||||
# 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
|
||||
|
||||
# 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))
|
||||
|
||||
# #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 - SELL
|
||||
# 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 SELL at", fill_time, "at",i[1])
|
||||
# break
|
||||
# if not fill_time: print("NO FILL for ", limit_price)
|
||||
|
||||
|
||||
# #MARKET FILL BUY/SELL:
|
||||
# for i in new_range:
|
||||
# #print(i)
|
||||
# #najde prvni nejvetsi čas vetsi nez minfill
|
||||
# if i[0] > submitted_at + 0.020:
|
||||
# #(1679081919.381649, 27.88)
|
||||
# print(i)
|
||||
# print("FILL MARKET at", i[0], "cena", i[1])
|
||||
# break
|
||||
|
||||
# del btdata[0:index_end]
|
||||
|
||||
|
||||
|
||||
# #0.0006699562072753906
|
||||
# #0.0007920265197753906
|
||||
|
||||
|
||||
# # (1679081913.290388, 27.8634)
|
||||
# # (1679081913.68588, 27.865)
|
||||
# # (1679081913.986394, 27.86)
|
||||
# # (1679081914.095521, 27.865)
|
||||
# # (1679081914.396844, 27.8601)
|
||||
# # (1679081914.601457, 27.865)
|
||||
# # (1679081914.721968, 27.86)
|
||||
# # (1679081914.739287, 27.86)
|
||||
# # (1679081914.739305, 27.865)
|
||||
# # (1679081914.739314, 27.865)
|
||||
# # (1679081914.73941, 27.865)*
|
||||
# # (1679081914.739554, 27.86)
|
||||
# # (1679081914.739569, 27.86)
|
||||
# # (1679081914.739572, 27.86)
|
||||
# # (1679081914.739635, 27.86)
|
||||
# # (1679081914.739644, 27.86)submit
|
||||
# # (1679081914.739771, 27.86)
|
||||
# # (1679081914.74, 27.865)
|
||||
# # (1679081914.74048, 27.865)
|
||||
# # (1679081914.740531, 27.865)
|
||||
# # (1679081914.740691, 27.865)
|
||||
# # (1679081914.746943, 27.865)
|
||||
# # (1679081914.779766, 27.86)
|
||||
# # (1679081914.779769, 27.86)
|
||||
# # (1679081914.779901, 27.86)
|
||||
# # (1679081914.779904, 27.865)
|
||||
# # (1679081914.77991, 27.865)
|
||||
# # (1679081914.780006, 27.865)
|
||||
# # (1679081914.780388, 27.865)
|
||||
# # (1679081914.780415, 27.865)
|
||||
# # (1679081914.79638, 27.86)
|
||||
# # (1679081914.79638, 27.86)
|
||||
# # (1679081914.796383, 27.865)
|
||||
# # (1679081914.796498, 27.865)
|
||||
# # (1679081914.796901, 27.865)
|
||||
# # (1679081914.816074, 27.865)
|
||||
# # (1679081914.942793, 27.865)
|
||||
# # (1679081915.424626, 27.8625)
|
||||
# # (1679081915.863117, 27.865)
|
||||
# # (1679081915.863255, 27.8675)
|
||||
# # (1679081915.870084, 27.865)
|
||||
# # (1679081915.877677, 27.865)
|
||||
# # (1679081916.015251, 27.865)
|
||||
# # (1679081916.018716, 27.865)
|
||||
# # (1679081916.494838, 27.8656)
|
||||
# # (1679081916.827929, 27.868)
|
||||
# # (1679081916.870675, 27.8636)
|
||||
# # (1679081917.140228, 27.87)
|
||||
# # (1679081917.140763, 27.87)
|
||||
# # (1679081917.150359, 27.865)end
|
||||
# # (1679081917.753467, 27.865)
|
||||
# # (1679081917.853001, 27.865)
|
||||
# # (1679081918.012672, 27.865)
|
||||
# # (1679081918.736837, 27.865)
|
||||
# # (1679081918.737011, 27.865)
|
||||
# # (1679081918.737177, 27.87)
|
||||
# # (1679081918.742472, 27.87)
|
||||
# # (1679081918.743335, 27.87)
|
||||
# # (1679081918.868673, 27.8699)
|
||||
# # (1679081919.01883, 27.87)
|
||||
# # (1679081919.018832, 27.87)
|
||||
# # (1679081919.018835, 27.87)
|
||||
# # (1679081919.018839, 27.87)
|
||||
# # (1679081919.018839, 27.87)
|
||||
# # (1679081919.018857, 27.87)
|
||||
# # (1679081919.018905, 27.87)
|
||||
# # (1679081919.018911, 27.87)
|
||||
# # (1679081919.018911, 27.87)
|
||||
# # (1679081919.018914, 27.87)
|
||||
# # (1679081919.018914, 27.87)
|
||||
# # (1679081919.01892, 27.87)
|
||||
# # (1679081919.01892, 27.87)
|
||||
# # (1679081919.018923, 27.87)
|
||||
# # (1679081919.018929, 27.87)
|
||||
# # (1679081919.018932, 27.87)
|
||||
# # (1679081919.018938, 27.87)
|
||||
# # (1679081919.018941, 27.87)
|
||||
# # (1679081919.018947, 27.87)
|
||||
# # (1679081919.01895, 27.87)
|
||||
# # (1679081919.018956, 27.87)
|
||||
# # (1679081919.018968, 27.87)
|
||||
# # (1679081919.018986, 27.87)
|
||||
# # (1679081919.019074, 27.87)
|
||||
# # (1679081919.019077, 27.87)
|
||||
# # (1679081919.019077, 27.87)
|
||||
# # (1679081919.019079, 27.87)
|
||||
# # (1679081919.019082, 27.87)
|
||||
# # (1679081919.019082, 27.87)
|
||||
# # (1679081919.019095, 27.87)
|
||||
# # (1679081919.019095, 27.87)
|
||||
# # (1679081919.0191, 27.87)
|
||||
# # (1679081919.019103, 27.87)
|
||||
# # (1679081919.019106, 27.87)
|
||||
# # (1679081919.019109, 27.87)
|
||||
# # (1679081919.019112, 27.87)
|
||||
# # (1679081919.019112, 27.87)
|
||||
# # (1679081919.019124, 27.87)
|
||||
# # (1679081919.019127, 27.87)
|
||||
# # (1679081919.019133, 27.87)
|
||||
# # (1679081919.019139, 27.87)
|
||||
# # (1679081919.019323, 27.87)
|
||||
# # (1679081919.019323, 27.87)
|
||||
# # (1679081919.019323, 27.87)
|
||||
# # (1679081919.019323, 27.87)
|
||||
# # (1679081919.019326, 27.87)
|
||||
# # (1679081919.019326, 27.87)
|
||||
# # (1679081919.019936, 27.87)
|
||||
# # (1679081919.019978, 27.87)
|
||||
# # (1679081919.020189, 27.87)
|
||||
# # (1679081919.020264, 27.87)
|
||||
# # (1679081919.020312, 27.87)
|
||||
# # (1679081919.020628, 27.87)
|
||||
# # (1679081919.025445, 27.87)
|
||||
# # (1679081919.02565, 27.87)
|
||||
# # (1679081919.066583, 27.87)
|
||||
# # (1679081919.066953, 27.87)
|
||||
# # (1679081919.067248, 27.87)
|
||||
# # (1679081919.067398, 27.875)
|
||||
# # (1679081919.067672, 27.875)
|
||||
# # (1679081919.067939, 27.875)
|
||||
# # (1679081919.067975, 27.875)
|
||||
# # (1679081919.071849, 27.875)
|
||||
# # (1679081919.157709, 27.875)
|
||||
# # (1679081919.184806, 27.875)
|
||||
# # (1679081919.301574, 27.87)
|
||||
# # (1679081919.381201, 27.88)
|
||||
# # (1679081919.381204, 27.88)
|
||||
# # (1679081919.381237, 27.88)
|
||||
# # (1679081919.381264, 27.875)
|
||||
# # (1679081919.381643, 27.88)
|
||||
# # (1679081919.381649, 27.88)
|
||||
# # (1679081919.381676, 27.88)
|
||||
# # (1679081919.381685, 27.88)
|
||||
# # (1679081919.381697, 27.88)
|
||||
# # (1679081919.381706, 27.88)
|
||||
# # (1679081919.381718, 27.88)
|
||||
# # (1679081919.395142, 27.875)
|
||||
# # (1679081919.469476, 27.88)
|
||||
# # (1679081919.570886, 27.88)
|
||||
# # (1679081919.690577, 27.875)
|
||||
# # (1679081920.168907, 27.878)
|
||||
22
testy/testServices.py
Normal file
22
testy/testServices.py
Normal file
@ -0,0 +1,22 @@
|
||||
from v2realbot.utils.utils import AttributeDict, zoneNY, dict_replace_value, Store, parse_toml_string
|
||||
import os,sys
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from v2realbot.enums.enums import Mode, Account
|
||||
from v2realbot.config import WEB_API_KEY
|
||||
from datetime import datetime
|
||||
from icecream import install, ic
|
||||
import os
|
||||
from rich import print
|
||||
from threading import current_thread
|
||||
from fastapi import FastAPI, Depends, HTTPException, status
|
||||
from fastapi.security import APIKeyHeader
|
||||
import uvicorn
|
||||
from uuid import UUID
|
||||
import controller.services as cs
|
||||
from v2realbot.common.model import StrategyInstance, RunnerView
|
||||
|
||||
d = "[stratvars] maxpozic = 205 chunk = 114 MA = 2 Trend = 3 profit = 0.02 lastbuyindex=-6 pendingbuys={} limitka = 'None' jevylozeno=0 vykladka=5 curve = [0.01, 0.01, 0.01, 0.0, 0.02, 0.02, 0.01,0.01, 0.01,0.03, 0.01, 0.01, 0.01,0.04, 0.01,0.01, 0.01,0.05, 0.01,0.01, 0.01,0.01, 0.06,0.01, 0.01,0.01, 0.01] blockbuy = 0 ticks2reset = 0.04"
|
||||
d='[stratvars]\r\n maxpozic = 200\r\n chunk = 111\r\n MA = 2\r\n Trend = 3\r\n profit = 0.02\r\n lastbuyindex=-6\r\n pendingbuys={}\r\n limitka = "None"\r\n jevylozeno=0\r\n vykladka=5\r\n curve = [0.01, 0.01, 0.01, 0.0, 0.02, 0.02, 0.01,0.01, 0.01,0.03, 0.01, 0.01, 0.01,0.04, 0.01,0.01, 0.01,0.05, 0.01,0.01, 0.01,0.01, 0.06,0.01, 0.01,0.01, 0.01]\r\n blockbuy = 0\r\n ticks2reset = 0.04'
|
||||
print(d)
|
||||
a,b = parse_toml_string(d)
|
||||
print(a,b)
|
||||
57
testy/testStore
Normal file
57
testy/testStore
Normal file
@ -0,0 +1,57 @@
|
||||
import os,sys
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
import pickle
|
||||
from v2realbot.common.model import StrategyInstance
|
||||
from typing import List, Self
|
||||
|
||||
#class to persist
|
||||
class Store:
|
||||
def __init__(self) -> None:
|
||||
self.silist : List[StrategyInstance] = None
|
||||
self.db_file ="cache/strategyinstances.cache"
|
||||
if os.path.exists(self.db_file):
|
||||
with open (self.db_file, 'rb') as fp:
|
||||
self.silist = pickle.load(fp)
|
||||
|
||||
def save(self):
|
||||
with open(self.db_file, 'wb') as fp:
|
||||
pickle.dump(self.silist, fp)
|
||||
|
||||
db = Store()
|
||||
print(db.silist)
|
||||
db.silist.append(StrategyInstance(
|
||||
id2=1,
|
||||
name="DD",
|
||||
symbol="DD",
|
||||
class_name="DD",
|
||||
script="DD",
|
||||
open_rush=1,
|
||||
close_rush=1,
|
||||
stratvars_conf="DD",
|
||||
add_data_conf="DD"))
|
||||
|
||||
print(db.silist)
|
||||
db.silist = []
|
||||
print(len(db.silist))
|
||||
db.save()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# class Neco:
|
||||
# def __init__(self) -> None:
|
||||
# pass
|
||||
# a = 1
|
||||
# b = 2
|
||||
|
||||
# def toJson(self):
|
||||
# return json.dumps(self, default=lambda o: o.__dict__)
|
||||
|
||||
# db.append(Neco.a)
|
||||
|
||||
# db.append(Neco.b)
|
||||
|
||||
# db.append(Neco)
|
||||
|
||||
# print(Neco)
|
||||
12
testy/testTIMIT
Normal file
12
testy/testTIMIT
Normal file
@ -0,0 +1,12 @@
|
||||
import timeit
|
||||
setup = '''
|
||||
import msgpack
|
||||
import json
|
||||
from copy import deepcopy
|
||||
data = {'name':'John Doe','ranks':{'sports':13,'edu':34,'arts':45},'grade':5}'''
|
||||
print(timeit.timeit('deepcopy(data)', setup=setup))
|
||||
# 12.0860249996
|
||||
print(timeit.timeit('json.loads(json.dumps(data))', setup=setup))
|
||||
# 9.07182312012
|
||||
print(timeit.timeit('msgpack.unpackb(msgpack.packb(data))', setup=setup))
|
||||
# 1.42743492126
|
||||
86
testy/testloadfromcache.py
Normal file
86
testy/testloadfromcache.py
Normal file
@ -0,0 +1,86 @@
|
||||
from datetime import date, timedelta
|
||||
from alpaca.data.historical import CryptoHistoricalDataClient, StockHistoricalDataClient
|
||||
from alpaca.data.requests import CryptoLatestTradeRequest, StockLatestTradeRequest, StockLatestBarRequest, StockTradesRequest
|
||||
from alpaca.data.enums import DataFeed
|
||||
from alpaca.trading.requests import GetCalendarRequest
|
||||
from alpaca.trading.client import TradingClient
|
||||
from alpaca.trading.models import Calendar
|
||||
from config import API_KEY, SECRET_KEY, MAX_BATCH_SIZE
|
||||
from datetime import datetime, timezone, time, timedelta, date
|
||||
import pytz
|
||||
from rich import print
|
||||
import os
|
||||
from icecream import install, ic
|
||||
install()
|
||||
import os
|
||||
# print('Get current working directory : ', os.getcwd())
|
||||
# print('File name : ', os.path.basename(__file__))
|
||||
# print('Directory Name: ', os.path.dirname(__file__))
|
||||
|
||||
#práce s datumy
|
||||
|
||||
zone_NY = pytz.timezone('America/New_York')
|
||||
|
||||
symbol = "BAC"
|
||||
client = TradingClient(API_KEY, SECRET_KEY, raw_data=False)
|
||||
datetime_object_from = datetime(2023, 3, 16, 17, 51, 38, tzinfo=timezone.utc)
|
||||
datetime_object_to = datetime(2023, 3, 22, 17, 52, 39, tzinfo=timezone.utc)
|
||||
calendar_request = GetCalendarRequest(start=datetime_object_from,end=datetime_object_to)
|
||||
cal_dates = client.get_calendar(calendar_request)
|
||||
#curr_dir = os.path.dirname(__file__)
|
||||
|
||||
#backtesting a obecne prace startegie s dnem
|
||||
#zatim podporime pouze main session
|
||||
|
||||
#backtest
|
||||
#- market open trade - Q
|
||||
#- market close trade - M
|
||||
|
||||
#minimalni jednotka pro CACHE je 1 den - a to jen marketopen to marketclose (extended hours not supported yet)
|
||||
for day in cal_dates:
|
||||
print("Processing DAY", day.date)
|
||||
print(day.date)
|
||||
print(day.open)
|
||||
print(day.close)
|
||||
|
||||
#get file name
|
||||
daily_file = str(symbol) + '-' + str(int(day.open.timestamp())) + '-' + str(int(day.close.timestamp())) + '.cache'
|
||||
print(daily_file)
|
||||
|
||||
|
||||
if os.path.exists(daily_file):
|
||||
pass
|
||||
##denní file existuje
|
||||
#loadujeme ze souboru
|
||||
#pokud je start_time < trade < end_time
|
||||
#odesíláme do queue
|
||||
#jinak pass
|
||||
else:
|
||||
ic("cache not exists")
|
||||
#denni file není - loadujeme den z Alpacy
|
||||
#ukládáme do cache s daily_file jako název
|
||||
#pokud jde o dnešní den a nebyl konec trhu tak cache neukládáme
|
||||
if datetime.now() < day.close:
|
||||
print("not saving the cache, market still open today")
|
||||
ic(datetime.now())
|
||||
ic(day.close)
|
||||
else:
|
||||
pass
|
||||
#save to daily cache file curr_dir+'/'+daily_file
|
||||
|
||||
#pokud je start_time < trade < end_time
|
||||
#odesíláme do queue
|
||||
#jinak ne
|
||||
|
||||
print("Processing DAY END",day.date)
|
||||
|
||||
# start_date = date(2008, 8, 15)
|
||||
# end_date = date(2008, 9, 15) # perhaps date.now()
|
||||
# ##get number of days between days
|
||||
# delta = end_date - start_date # returns timedelta
|
||||
|
||||
# for i in range(delta.days + 1):
|
||||
# day = start_date + timedelta(days=i)
|
||||
# print(day)
|
||||
|
||||
# #pro kazde datum volame get cala - jestli byl trader date
|
||||
165
testy/threadclassestest.py
Normal file
165
testy/threadclassestest.py
Normal file
@ -0,0 +1,165 @@
|
||||
from threading import Thread, current_thread
|
||||
import threading
|
||||
from alpaca.data.live import StockDataStream, CryptoDataStream
|
||||
from v2realbot.config import API_KEY, SECRET_KEY, MAX_BATCH_SIZE, PAPER
|
||||
import queue
|
||||
from alpaca.data.enums import DataFeed
|
||||
from typing_extensions import Any
|
||||
import time
|
||||
from v2realbot.loader.aggregator import TradeAggregator2Queue
|
||||
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Order
|
||||
|
||||
|
||||
# class ws_agg() :
|
||||
# def __init__(self, client, symbol) -> None:
|
||||
# # Call the Thread class's init function
|
||||
# Thread.__init__(self)
|
||||
# self.client = client
|
||||
# self.symbol = symbol
|
||||
|
||||
|
||||
|
||||
#object composition
|
||||
|
||||
"""""
|
||||
vlakno je zde pro asynchronni zapnuti klienta,
|
||||
vlakno je vzdy pouze jedno, nicmene instancovani teto tridy je kvuli stejnemu chovani
|
||||
s ostatnimi streamery (v budoucnu mozna predelat na dedicated streamer a shared streamer)
|
||||
"""""
|
||||
class WS_Stream(Thread):
|
||||
client = CryptoDataStream(API_KEY, SECRET_KEY, raw_data=True, websocket_params={})
|
||||
_streams = []
|
||||
lock = threading.Lock()
|
||||
|
||||
def __init__(self, name) -> None:
|
||||
# Call the Thread class's init function
|
||||
Thread.__init__(self, name=name)
|
||||
#promenna bude obsahovat seznam streamů
|
||||
name = name
|
||||
|
||||
def symbol_exists(self, symbol):
|
||||
for i in WS_Stream._streams:
|
||||
if i.symbol == symbol:
|
||||
return True
|
||||
return False
|
||||
|
||||
def add_stream(self, obj):
|
||||
WS_Stream._streams.append(obj)
|
||||
if WS_Stream.client._running is False:
|
||||
print("websocket zatim nebezi, pridavame do pole")
|
||||
#do promenne tridy se zapise agregator
|
||||
else:
|
||||
print("websokcet bezi - pouze subscribujeme")
|
||||
WS_Stream.client.subscribe_trades(self.handler, obj.symbol)
|
||||
print("muze se vratit uz subscribnuto, coz je ok")
|
||||
|
||||
def remove_stream(self, obj):
|
||||
#delete added stream
|
||||
try:
|
||||
WS_Stream._streams.remove(obj)
|
||||
except ValueError:
|
||||
print("value not found in _streams")
|
||||
return
|
||||
#if it is the last item at all, stop the client from running
|
||||
if len( WS_Stream._streams) == 0:
|
||||
print("removed last item from WS, stopping the client")
|
||||
WS_Stream.client.stop()
|
||||
return
|
||||
|
||||
if not self.symbol_exists(obj.symbol):
|
||||
WS_Stream.client.unsubscribe_trades(obj.symbol)
|
||||
print("symbol no longer used, unsubscribed from ", obj.symbol)
|
||||
|
||||
@classmethod
|
||||
async def handler(cls, data):
|
||||
print("handler ve threadu:",current_thread().name)
|
||||
# podíváme kolik streamů je instancovaných pro tento symbol - v dict[symbol] a spusteni
|
||||
# pro každý stream zavoláme
|
||||
|
||||
print(data)
|
||||
print("*"*40)
|
||||
|
||||
def run(self):
|
||||
print(self.name, "AKtualni vlakno")
|
||||
if(len(self._streams)==0):
|
||||
print(self.name, "no streams. no run")
|
||||
return
|
||||
#print(self._streams)
|
||||
unique = set()
|
||||
## for each symbol we subscribe
|
||||
for i in self._streams:
|
||||
#print(self.name, i.symbol)
|
||||
#instanciace tradeAggregatoru a uložení do dict[symbol]
|
||||
#zde
|
||||
unique.add(i.symbol)
|
||||
#print(unique)
|
||||
#subscribe for unique symbols
|
||||
|
||||
#
|
||||
##TODO *PROBLEM* co kdyz chci subscribe stejneho symbolu co uz konzumuje jina strategie. PROBLEM koncepční
|
||||
##TODO pri skonceni jedne strategie, udelat teardown kroky jako unsubscribe pripadne stop
|
||||
for i in unique:
|
||||
WS_Stream.client.subscribe_trades(self.handler, i)
|
||||
print(self.name, "subscribed to",i)
|
||||
#timto se spusti jenom poprve v 1 vlaknu
|
||||
#ostatni pouze vyuzivaji
|
||||
if WS_Stream.client._running is False:
|
||||
print(self.name, "it is not running, starting by calling RUN")
|
||||
WS_Stream.client.run()
|
||||
#tímto se spustí pouze 1.vlakno, nicmene subscribe i pripadny unsubscribe zafunguji
|
||||
else:
|
||||
print(self.name, "it is running, not calling RUN")
|
||||
|
||||
|
||||
# class SymbolStream():
|
||||
# def __init__(self, symbol) -> None:
|
||||
# self.symbol = symbol
|
||||
# s
|
||||
# class StreamRequest:
|
||||
# symbol: str
|
||||
# resolution: int
|
||||
|
||||
#clientDataStream = CryptoDataStream(API_KEY, SECRET_KEY, raw_data=True, websocket_params={})
|
||||
|
||||
# novy ws stream - vždy jednom vláknu
|
||||
obj= WS_Stream(name="jednicka")
|
||||
q1 = queue.Queue()
|
||||
stream1 = TradeAggregator2Queue(symbol="BTC/USD",queue=q1,rectype=RecordType.BAR,timeframe=15,update_ltp=False,align=StartBarAlign.ROUND,mintick = 0, mode = Mode.LIVE)
|
||||
obj.add_stream(stream1)
|
||||
print("1", WS_Stream._streams)
|
||||
# novy ws stream - vždy jednom vláknu
|
||||
obj2= WS_Stream("dvojka")
|
||||
stream2 = TradeAggregator2Queue(symbol="ETH/USD",queue=q1,rectype=RecordType.BAR,timeframe=5,update_ltp=False,align=StartBarAlign.ROUND,mintick = 0, mode = Mode.LIVE)
|
||||
obj2.add_stream(stream2)
|
||||
print("2", WS_Stream._streams)
|
||||
obj.start()
|
||||
print("po startu prvniho")
|
||||
print(WS_Stream._streams)
|
||||
time.sleep(1)
|
||||
obj2.start()
|
||||
print("po startu druheho")
|
||||
time.sleep(2)
|
||||
print("pridavame treti")
|
||||
obj3 = WS_Stream(name="trojka")
|
||||
stream3 = TradeAggregator2Queue(symbol="BTC/USD",queue=q1,rectype=RecordType.BAR,timeframe=1,update_ltp=False,align=StartBarAlign.ROUND,mintick = 0, mode = Mode.LIVE)
|
||||
obj3.add_stream(stream3)
|
||||
obj3.start()
|
||||
print(WS_Stream._streams)
|
||||
print("po zapnuti trojky")
|
||||
time.sleep(5)
|
||||
print("cekame na skonceni")
|
||||
print("celkem enumerate", threading.enumerate())
|
||||
time.sleep(2)
|
||||
print("rusim jednicku")
|
||||
obj.remove_stream(stream1)
|
||||
print("po ruseni")
|
||||
time.sleep(2)
|
||||
print("rusim dvojku")
|
||||
obj2.remove_stream(stream2)
|
||||
print("po ruseni")
|
||||
time.sleep(2)
|
||||
print("rusim trojku")
|
||||
obj3.remove_stream(stream3)
|
||||
obj2.join()
|
||||
obj.join()
|
||||
|
||||
111
testy/websocketFastApi.py
Normal file
111
testy/websocketFastApi.py
Normal file
@ -0,0 +1,111 @@
|
||||
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Depends, HTTPException, status, WebSocketException, Cookie, Query
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||
import secrets
|
||||
from typing import Annotated
|
||||
import os
|
||||
import uvicorn
|
||||
import json
|
||||
from datetime import datetime
|
||||
from v2realbot.utils.utils import zoneNY
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
root = os.path.dirname(os.path.abspath(__file__))
|
||||
app.mount("/static", StaticFiles(directory=os.path.join(root, 'static')), name="static")
|
||||
|
||||
|
||||
html = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Chat</title>
|
||||
<script type="text/javascript" src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"
|
||||
></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Realtime chart</h1>
|
||||
<h2>Your ID: <span id="ws-id"></span></h2>
|
||||
<h3>Status: <span id="status">Not connected</span></h3>
|
||||
<form action="" onsubmit="sendMessage(event)">
|
||||
<label>Runner ID: <input type="text" id="runnerId" autocomplete="off" value="foo"/></label>
|
||||
<label>Token: <input type="text" id="token" autocomplete="off" value="some-key-token"/></label>
|
||||
<button onclick="connect(event)" id="bt-conn">Connect</button>
|
||||
<button onclick="disconnect(event)" id="bt-disc" style="display: None">Disconnect</button>
|
||||
<hr>
|
||||
<label>Message: <input type="text" id="messageText" autocomplete="off"/></label>
|
||||
<button>Send</button>
|
||||
</form>
|
||||
<ul id='messages'>
|
||||
</ul>
|
||||
<div id="chart"></div>
|
||||
<div id="conteiner"></div>
|
||||
<script src="/static/js/mywebsocket.js"></script>
|
||||
<script src="/static/js/mychart.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
security = HTTPBasic()
|
||||
|
||||
async def get_cookie_or_token(
|
||||
websocket: WebSocket,
|
||||
session: Annotated[str | None, Cookie()] = None,
|
||||
token: Annotated[str | None, Query()] = None,
|
||||
):
|
||||
if session is None and token is None:
|
||||
raise WebSocketException(code=status.WS_1008_POLICY_VIOLATION)
|
||||
return session or token
|
||||
|
||||
def get_current_username(
|
||||
credentials: Annotated[HTTPBasicCredentials, Depends(security)]
|
||||
):
|
||||
if not (credentials.username == "david") or not (credentials.password == "david"):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Incorrect email or password",
|
||||
headers={"WWW-Authenticate": "Basic"},
|
||||
)
|
||||
|
||||
@app.get("/")
|
||||
async def get(username: Annotated[str, Depends(get_current_username)]):
|
||||
return HTMLResponse(html)
|
||||
|
||||
|
||||
@app.websocket("/runners/{runner_id}/ws")
|
||||
async def websocket_endpoint(
|
||||
*,
|
||||
websocket: WebSocket,
|
||||
runner_id: str,
|
||||
q: int | None = None,
|
||||
cookie_or_token: Annotated[str, Depends(get_cookie_or_token)],
|
||||
):
|
||||
await websocket.accept()
|
||||
try:
|
||||
while True:
|
||||
data = await websocket.receive_text()
|
||||
await websocket.send_text(
|
||||
f"Session cookie or query token value is: {cookie_or_token}"
|
||||
)
|
||||
if q is not None:
|
||||
await websocket.send_text(f"Query parameter q is: {q}")
|
||||
data = {'high': 195,
|
||||
'low': 180,
|
||||
'volume': 123,
|
||||
'close': 185,
|
||||
'hlcc4': 123,
|
||||
'open': 190,
|
||||
'time': "2019-05-25",
|
||||
'trades':123,
|
||||
'resolution':123,
|
||||
'confirmed': 123,
|
||||
'vwap': 123,
|
||||
'updated': 123,
|
||||
'index': 123}
|
||||
await websocket.send_text(json.dumps(data))
|
||||
except WebSocketDisconnect:
|
||||
print("CLIENT DISCONNECTED for", runner_id)
|
||||
|
||||
if __name__ == "__main__":
|
||||
uvicorn.run("__main__:app", host="0.0.0.0", port=8000, reload=False)
|
||||
111
testy/websocketFastApiConnManager.py
Normal file
111
testy/websocketFastApiConnManager.py
Normal file
@ -0,0 +1,111 @@
|
||||
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, Depends, HTTPException, status
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.security import HTTPBasic, HTTPBasicCredentials
|
||||
import secrets
|
||||
from typing import Annotated
|
||||
import os
|
||||
import uvicorn
|
||||
import json
|
||||
from datetime import datetime
|
||||
from v2realbot.utils.utils import zoneNY
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
root = os.path.dirname(os.path.abspath(__file__))
|
||||
app.mount("/static", StaticFiles(directory=os.path.join(root, 'static')), name="static")
|
||||
|
||||
|
||||
html = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Chat</title>
|
||||
<script type="text/javascript" src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"
|
||||
></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Realtime chart</h1>
|
||||
<h2>Your ID: <span id="ws-id"></span></h2>
|
||||
<form action="" onsubmit="sendMessage(event)">
|
||||
<input type="text" id="messageText" autocomplete="off"/>
|
||||
<button>Send</button>
|
||||
</form>
|
||||
<ul id='messages'>
|
||||
</ul>
|
||||
<div id="chart"></div>
|
||||
<div id="conteiner"></div>
|
||||
<script src="/static/js/mywebsocket.js"></script>
|
||||
<script src="/static/js/mychart.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
|
||||
class ConnectionManager:
|
||||
def __init__(self):
|
||||
self.active_connections: list[WebSocket] = []
|
||||
|
||||
async def connect(self, websocket: WebSocket):
|
||||
await websocket.accept()
|
||||
self.active_connections.append(websocket)
|
||||
|
||||
def disconnect(self, websocket: WebSocket):
|
||||
self.active_connections.remove(websocket)
|
||||
|
||||
async def send_personal_message(self, message: str, websocket: WebSocket):
|
||||
await websocket.send_text(message)
|
||||
|
||||
async def broadcast(self, message: str):
|
||||
for connection in self.active_connections:
|
||||
await connection.send_text(message)
|
||||
|
||||
manager = ConnectionManager()
|
||||
security = HTTPBasic()
|
||||
|
||||
def get_current_username(
|
||||
credentials: Annotated[HTTPBasicCredentials, Depends(security)]
|
||||
):
|
||||
if not (credentials.username == "david") or not (credentials.password == "david"):
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Incorrect email or password",
|
||||
headers={"WWW-Authenticate": "Basic"},
|
||||
)
|
||||
|
||||
@app.get("/")
|
||||
async def get(username: Annotated[str, Depends(get_current_username)]):
|
||||
return HTMLResponse(html)
|
||||
|
||||
|
||||
@app.websocket("/ws/{client_id}")
|
||||
async def websocket_endpoint(websocket: WebSocket, client_id: int):
|
||||
await manager.connect(websocket)
|
||||
try:
|
||||
while True:
|
||||
data = await websocket.receive_text()
|
||||
data = {'high': 195,
|
||||
'low': 180,
|
||||
'volume': 123,
|
||||
'close': 185,
|
||||
'hlcc4': 123,
|
||||
'open': 190,
|
||||
'time': "2019-05-25",
|
||||
'trades':123,
|
||||
'resolution':123,
|
||||
'confirmed': 123,
|
||||
'vwap': 123,
|
||||
'updated': 123,
|
||||
'index': 123}
|
||||
# data = {'high': 123,
|
||||
# 'low': 123,
|
||||
# 'close': 123,
|
||||
# 'open': 123,
|
||||
# 'time': "2019-05-25"}
|
||||
await manager.send_personal_message(json.dumps(data), websocket)
|
||||
#await manager.broadcast(f"Client #{client_id} says: {data}")
|
||||
except WebSocketDisconnect:
|
||||
manager.disconnect(websocket)
|
||||
await manager.broadcast(f"Client #{client_id} left the chat")
|
||||
|
||||
if __name__ == "__main__":
|
||||
uvicorn.run("__main__:app", host="0.0.0.0", port=8000, reload=False)
|
||||
90
v2realbot/ENTRY_backtest_strategy.py
Normal file
90
v2realbot/ENTRY_backtest_strategy.py
Normal file
@ -0,0 +1,90 @@
|
||||
from strategy.base import Strategy, StrategyState
|
||||
from strategy.strategyOrderLimit import StrategyOrderLimit
|
||||
from enums import RecordType, StartBarAlign, Mode
|
||||
from config import API_KEY, SECRET_KEY, MAX_BATCH_SIZE, PAPER
|
||||
from indicators import ema
|
||||
from rich import print
|
||||
from utils.utils import ltp, isrising, isfalling,trunc,AttributeDict, zoneNY
|
||||
from datetime import datetime
|
||||
from icecream import install, ic
|
||||
install()
|
||||
ic.configureOutput(includeContext=True)
|
||||
#ic.disable()
|
||||
""""
|
||||
Simple strategie pro test backtesting
|
||||
"""
|
||||
|
||||
def next(data, state: StrategyState):
|
||||
print(10*"*","NEXT START",10*"*")
|
||||
ic(state.avgp, state.positions)
|
||||
ic(state.vars.limitka)
|
||||
ic(state.vars.lastbuyindex)
|
||||
ic(data)
|
||||
#print("last trade price")
|
||||
#print(state.interface.get_last_price("BAC"))
|
||||
#print(state.vars.novaprom)
|
||||
#print("trades history", state.trades)
|
||||
#print("bar history", state.bars)
|
||||
#print("ltp", ltp.price["BAC"], ltp.time["BAC"])
|
||||
|
||||
#TODO indikátory ukládat do vlastní historie - tu pak automaticky zobrazuje backtester graf
|
||||
|
||||
#TODO ema = state.indicators.ema a pouzivat nize ema, zjistit jestli bude fungovat
|
||||
|
||||
try:
|
||||
state.indicators.ema = ema(state.bars.hlcc4, state.vars.MA) #state.bars.vwap
|
||||
#trochu prasarna, EMAcko trunc na 3 mista - kdyz se osvedci, tak udelat efektivne
|
||||
state.indicators.ema = [trunc(i,3) for i in state.indicators.ema]
|
||||
ic(state.vars.MA, state.vars.Trend, state.indicators.ema[-5:])
|
||||
except Exception as e:
|
||||
print("No data for MA yet", str(e))
|
||||
|
||||
print("is falling",isfalling(state.indicators.ema,state.vars.Trend))
|
||||
print("is rising",isrising(state.indicators.ema,state.vars.Trend))
|
||||
|
||||
#ZDE JSEM SKONCIL
|
||||
#nejprve zacit s BARy
|
||||
|
||||
#TODO vyzkoušet limit buy - vetsina z nakupu by se dala koupit o cent dva mene
|
||||
#proto dodělat LTP pro BT, neco jako get_last_price(self.state.time)
|
||||
|
||||
|
||||
##TODO vyzkouset hlidat si sell objednavku sam na zaklade tradu
|
||||
# v pripade ze to jde nahoru(is rising - nebo jiny indikator) tak neprodavat
|
||||
#vyuzit CBARy k tomuto .....
|
||||
#triggerovat buy treba po polovine CBARu, kdyz se cena bude rovnat nebo bude nizsi nez low
|
||||
#a hned na to (po potvrzeni) hlidat sell +0.01 nebo kdyz roste nechat rust.vyzkouset na LIVE
|
||||
|
||||
if isfalling(state.indicators.ema,state.vars.Trend) and data['index'] > state.vars.lastbuyindex+state.vars.Trend: #and state.blockbuy == 0
|
||||
print("BUY MARKET")
|
||||
ic(data['updated'])
|
||||
ic(state.time)
|
||||
state.buy_l()
|
||||
|
||||
|
||||
print(10*"*","NEXT STOP",10*"*")
|
||||
|
||||
def init(state: StrategyState):
|
||||
#place to declare new vars
|
||||
print("INIT v main",state.name)
|
||||
state.vars['novaprom'] = 4
|
||||
state.indicators['ema'] = []
|
||||
|
||||
def main():
|
||||
stratvars = AttributeDict(maxpozic = 2400, chunk = 400, MA = 6, Trend = 7, profit = 0.03, lastbuyindex=-6, pendingbuys={},limitka = None)
|
||||
|
||||
s = StrategyOrderLimit(name = "BackTEST", symbol = "KO", next=next, init=init, stratvars=stratvars, debug=False)
|
||||
s.set_mode(mode = Mode.BT,
|
||||
start = datetime(2023, 2, 23, 9, 30, 0, 0, tzinfo=zoneNY),
|
||||
end = datetime(2023, 2, 23, 16, 00, 0, 0, tzinfo=zoneNY),
|
||||
cash=100000)
|
||||
|
||||
#na sekundovem baru nezaokrouhlovat MAcko
|
||||
s.add_data(symbol="KO",rectype=RecordType.BAR,timeframe=30,filters=None,update_ltp=True,align=StartBarAlign.RANDOM,mintick=0)
|
||||
#s.add_data(symbol="C",rectype=RecordType.BAR,timeframe=1,filters=None,update_ltp=True,align=StartBarAlign.ROUND,mintick=0)
|
||||
|
||||
s.start()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
99
v2realbot/ENTRY_backtest_strategyKOKA-ok.py
Normal file
99
v2realbot/ENTRY_backtest_strategyKOKA-ok.py
Normal file
@ -0,0 +1,99 @@
|
||||
# import os,sys
|
||||
# sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from v2realbot.strategy.base import Strategy, StrategyState
|
||||
from v2realbot.strategy.StrategyOrderLimitKOKA import StrategyOrderLimitKOKA
|
||||
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode
|
||||
from v2realbot.indicators.indicators import ema
|
||||
from rich import print
|
||||
from v2realbot.utils.utils import ltp, isrising, isfalling,trunc,AttributeDict, zoneNY
|
||||
from datetime import datetime
|
||||
from icecream import install, ic
|
||||
import os
|
||||
install()
|
||||
ic.configureOutput(includeContext=True)
|
||||
#ic.disable()
|
||||
""""
|
||||
Simple strategie LIMIT buy a lIMIT SELL working ok
|
||||
|
||||
DOkupovaci strategie, nakupuje dalsi pozice až po dalším signálu.
|
||||
|
||||
POZOR nekontroluje se maximální pozice - tzn. nejvic se vycerpalo 290, ale prezila kazdy den.
|
||||
Dobrá defenzivní pri nastaveni
|
||||
30s maxpozic = 290,chunk = 10,MA = 6,Trend = 6,profit = 0.02,
|
||||
"""
|
||||
stratvars = AttributeDict(maxpozic = 250,
|
||||
chunk = 10,
|
||||
MA = 6,
|
||||
Trend = 6,
|
||||
profit = 0.02,
|
||||
lastbuyindex=-6,
|
||||
pendingbuys={},
|
||||
limitka = None)
|
||||
|
||||
def next(data, state: StrategyState):
|
||||
print(10*"*","NEXT START",10*"*")
|
||||
ic(state.avgp, state.positions)
|
||||
ic(state.vars.limitka)
|
||||
ic(state.vars.lastbuyindex)
|
||||
ic(data)
|
||||
#print("last trade price")
|
||||
#print(state.interface.get_last_price("BAC"))
|
||||
#print(state.vars.novaprom)
|
||||
#print("trades history", state.trades)
|
||||
#print("bar history", state.bars)
|
||||
#print("ltp", ltp.price["BAC"], ltp.time["BAC"])
|
||||
|
||||
#TODO indikátory ukládat do vlastní historie - tu pak automaticky zobrazuje backtester graf
|
||||
|
||||
#TODO ema = state.indicators.ema a pouzivat nize ema, zjistit jestli bude fungovat
|
||||
|
||||
try:
|
||||
state.indicators.ema = ema(state.bars.hlcc4, state.vars.MA) #state.bars.vwap
|
||||
#trochu prasarna, EMAcko trunc na 3 mista - kdyz se osvedci, tak udelat efektivne
|
||||
state.indicators.ema = [trunc(i,3) for i in state.indicators.ema]
|
||||
ic(state.vars.MA, state.vars.Trend, state.indicators.ema[-5:])
|
||||
except Exception as e:
|
||||
print("No data for MA yet", str(e))
|
||||
|
||||
print("is falling",isfalling(state.indicators.ema,state.vars.Trend))
|
||||
print("is rising",isrising(state.indicators.ema,state.vars.Trend))
|
||||
|
||||
#ZDE JSEM SKONCIL
|
||||
#nejprve zacit s BARy
|
||||
|
||||
#TODO vyzkoušet limit buy - vetsina z nakupu by se dala koupit o cent dva mene
|
||||
#proto dodělat LTP pro BT, neco jako get_last_price(self.state.time)
|
||||
if isfalling(state.indicators.ema,state.vars.Trend) and data['index'] > state.vars.lastbuyindex+state.vars.Trend: #and state.blockbuy == 0
|
||||
print("BUY MARKET")
|
||||
ic(data['updated'])
|
||||
ic(state.time)
|
||||
state.buy_l()
|
||||
|
||||
|
||||
print(10*"*","NEXT STOP",10*"*")
|
||||
|
||||
def init(state: StrategyState):
|
||||
#place to declare new vars
|
||||
print("INIT v main",state.name)
|
||||
state.vars['novaprom'] = 4
|
||||
state.indicators['ema'] = []
|
||||
|
||||
def main():
|
||||
name = os.path.basename(__file__)
|
||||
s = StrategyOrderLimitKOKA(name = name, symbol = "BAC", next=next, init=init, stratvars=stratvars, open_rush=30, close_rush=0)
|
||||
s.set_mode(mode = Mode.PAPER,
|
||||
debug = False,
|
||||
start = datetime(2023, 3, 6, 9, 30, 0, 0, tzinfo=zoneNY),
|
||||
end = datetime(2023, 3, 9, 16, 0, 0, 0, tzinfo=zoneNY),
|
||||
cash=100000)
|
||||
|
||||
#na sekundovem baru nezaokrouhlovat MAcko
|
||||
s.add_data(symbol="BAC",rectype=RecordType.BAR,timeframe=30,filters=None,update_ltp=True,align=StartBarAlign.ROUND,mintick=0)
|
||||
#s.add_data(symbol="C",rectype=RecordType.BAR,timeframe=1,filters=None,update_ltp=True,align=StartBarAlign.ROUND,mintick=0)
|
||||
|
||||
s.start()
|
||||
print("zastavujeme")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
243
v2realbot/ENTRY_backtest_strategyVykladaci.py
Normal file
243
v2realbot/ENTRY_backtest_strategyVykladaci.py
Normal file
@ -0,0 +1,243 @@
|
||||
import os,sys
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
from v2realbot.strategy.base import StrategyState
|
||||
from v2realbot.strategy.StrategyOrderLimitVykladaci import StrategyOrderLimitVykladaci
|
||||
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account
|
||||
from v2realbot.indicators.indicators import ema
|
||||
from v2realbot.utils.utils import ltp, isrising, isfalling,trunc,AttributeDict, zoneNY, price2dec, dict_replace_value
|
||||
from datetime import datetime
|
||||
from icecream import install, ic
|
||||
from rich import print
|
||||
from threading import Event
|
||||
import asyncio
|
||||
import os
|
||||
import tomli
|
||||
install()
|
||||
ic.configureOutput(includeContext=True)
|
||||
#ic.disable()
|
||||
|
||||
print(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
""""
|
||||
Vykladaci strategie refactored z původního engine
|
||||
Params:
|
||||
(maxpozic = 200, chunk = 50, MA = 6, Trend = 6, profit = 0.02, lastbuyindex=-6, pendingbuys={},limitka = None, jevylozeno=0, ticks2reset = 0.04, blockbuy=0)
|
||||
|
||||
|
||||
Více nakupuje oproti Dokupovaci. Tady vylozime a nakupujeme 5 pozic hned. Pri dokupovaci se dokupuje, az na zaklade dalsich triggeru.
|
||||
Do budoucna vice ridit nakupy pri klesani - napr. vyložení jen 2-3 pozic a další dokupy až po triggeru.
|
||||
#
|
||||
"""
|
||||
stratvars = AttributeDict(maxpozic = 250,
|
||||
chunk = 10,
|
||||
MA = 3,
|
||||
Trend = 3,
|
||||
profit = 0.02,
|
||||
lastbuyindex=-6,
|
||||
pendingbuys={},
|
||||
limitka = None,
|
||||
jevylozeno=0,
|
||||
vykladka=5,
|
||||
curve = [0.01, 0.01, 0.01, 0, 0.02, 0.02, 0.01,0.01, 0.01,0.03, 0.01, 0.01, 0.01,0.04, 0.01,0.01, 0.01,0.05, 0.01,0.01, 0.01,0.01, 0.06,0.01, 0.01,0.01, 0.01],
|
||||
blockbuy = 0,
|
||||
ticks2reset = 0.04)
|
||||
##toto rozparsovat a strategii spustit stejne jako v main
|
||||
toml_string = """
|
||||
[[strategies]]
|
||||
name = "V1 na BAC"
|
||||
symbol = "BAC"
|
||||
script = "ENTRY_backtest_strategyVykladaci"
|
||||
class = "StrategyOrderLimitVykladaci"
|
||||
open_rush = 0
|
||||
close_rush = 0
|
||||
[strategies.stratvars]
|
||||
maxpozic = 200
|
||||
chunk = 10
|
||||
MA = 6
|
||||
Trend = 5
|
||||
profit = 0.02
|
||||
lastbuyindex=-6
|
||||
pendingbuys={}
|
||||
limitka = "None"
|
||||
jevylozeno=0
|
||||
vykladka=5
|
||||
curve = [0.01, 0.01, 0.01,0.01, 0.02, 0.01,0.01, 0.01,0.03, 0.01, 0.01, 0.01,0.04, 0.01,0.01, 0.01,0.05, 0.01,0.01, 0.01,0.01, 0.06,0.01, 0.01,0.01, 0.01]
|
||||
blockbuy = 0
|
||||
ticks2reset = 0.04
|
||||
[[strategies.add_data]]
|
||||
symbol="BAC"
|
||||
rectype="bar"
|
||||
timeframe=5
|
||||
update_ltp=true
|
||||
align="round"
|
||||
mintick=0
|
||||
minsize=100
|
||||
exthours=false
|
||||
"""
|
||||
|
||||
def next(data, state: StrategyState):
|
||||
print(10*"*","NEXT START",10*"*")
|
||||
ic(state.avgp, state.positions)
|
||||
ic(state.vars)
|
||||
ic(data)
|
||||
|
||||
#mozna presunout o level vys
|
||||
def vyloz():
|
||||
##prvni se vyklada na aktualni cenu, další jdou podle krivky, nula v krivce zvyšuje množství pro následující iteraci
|
||||
#curve = [0.01, 0.01, 0, 0, 0.01, 0, 0, 0, 0.02, 0, 0, 0, 0.03, 0,0,0,0,0, 0.02, 0,0,0,0,0,0, 0.02]
|
||||
curve = state.vars.curve
|
||||
|
||||
#vykladani po 5ti kusech, když zbývají 2 a méně, tak děláme nový výklad
|
||||
vykladka = state.vars.vykladka
|
||||
#kolik muzu max vylozit
|
||||
kolikmuzu = int((int(state.vars.maxpozic) - int(state.positions))/int(state.vars.chunk))
|
||||
if kolikmuzu < vykladka: vykladka = kolikmuzu
|
||||
|
||||
if len(curve) < vykladka:
|
||||
vykladka = len(curve)
|
||||
qty = int(state.vars.chunk)
|
||||
last_price = price2dec(state.interface.get_last_price(state.symbol))
|
||||
#profit = float(state.vars.profit)
|
||||
price = last_price
|
||||
##prvni se vyklada na aktualni cenu, další jdou podle krivky, nula v krivce zvyšuje množství pro následující iteraci
|
||||
state.buy_l(price=price, size=qty)
|
||||
print("prvni limitka na aktuální cenu. Další podle křicvky", price, qty)
|
||||
for i in range(0,vykladka-1):
|
||||
price = price2dec(float(price - curve[i]))
|
||||
if price == last_price:
|
||||
qty = qty + int(state.vars.chunk)
|
||||
else:
|
||||
state.buy_l(price=price, size=qty)
|
||||
print(i,"BUY limitka - delta",curve[i]," cena:", price, "mnozstvi:", qty)
|
||||
qty = int(state.vars.chunk)
|
||||
last_price = price
|
||||
state.vars.blockbuy = 1
|
||||
state.vars.jevylozeno = 1
|
||||
|
||||
#CBAR protection, only 1x order per CBAR - then wait until another confirmed bar
|
||||
|
||||
if state.vars.blockbuy == 1 and state.rectype == RecordType.CBAR:
|
||||
if state.bars.confirmed[-1] == 0:
|
||||
print("OCHR: multibuy protection. waiting for next bar")
|
||||
return 0
|
||||
# pop potvrzeni jeste jednou vratime (aby se nekoupilo znova, je stale ten stejny bar)
|
||||
# a pak dalsi vejde az po minticku
|
||||
else:
|
||||
# pro vykladaci
|
||||
state.vars.blockbuy = 0
|
||||
return 0
|
||||
|
||||
try:
|
||||
print(state.vars.MA, "MACKO")
|
||||
print(state.bars.hlcc4)
|
||||
state.indicators.ema = ema(state.bars.hlcc4, state.vars.MA) #state.bars.vwap
|
||||
#trochu prasarna, EMAcko trunc na 3 mista - kdyz se osvedci, tak udelat efektivne
|
||||
state.indicators.ema = [trunc(i,3) for i in state.indicators.ema]
|
||||
ic(state.vars.MA, state.vars.Trend, state.indicators.ema[-5:])
|
||||
except Exception as e:
|
||||
print("No data for MA yet", str(e))
|
||||
|
||||
print("is falling",isfalling(state.indicators.ema,state.vars.Trend))
|
||||
print("is rising",isrising(state.indicators.ema,state.vars.Trend))
|
||||
|
||||
#and data['index'] > state.vars.lastbuyindex+state.vars.Trend:
|
||||
#neni vylozeno muzeme nakupovat
|
||||
if ic(state.vars.jevylozeno) == 0:
|
||||
print("Neni vylozeno, muzeme testovat nakup")
|
||||
|
||||
if isfalling(state.indicators.ema,state.vars.Trend):
|
||||
print("BUY MARKET")
|
||||
ic(data['updated'])
|
||||
ic(state.time)
|
||||
#zatim vykladame full
|
||||
#positions = int(int(state.vars.maxpozic)/int(state.vars.chunk))
|
||||
vyloz()
|
||||
|
||||
## testuje aktualni cenu od nejvyssi visici limitky
|
||||
##toto spoustet jednou za X iterací - ted to jede pokazdé
|
||||
#pokud to ujede o vic, rusime limitky
|
||||
#TODO: zvazit jestli nechat i pri otevrenych pozicich, zatim nechavame
|
||||
#TODO int(int(state.oa.poz)/int(state.variables.chunk)) > X
|
||||
|
||||
#TODO predelat mechanismus ticků (zrelativizovat), aby byl pouzitelny na tituly s ruznou cenou
|
||||
#TODO spoustet 1x za X iteraci nebo cas
|
||||
if state.vars.jevylozeno == 1:
|
||||
#pokud mame vylozeno a cena je vetsi nez 0.04
|
||||
if len(state.vars.pendingbuys)>0:
|
||||
maxprice = max(state.vars.pendingbuys.values())
|
||||
print("max cena v orderbuys", maxprice)
|
||||
if state.interface.get_last_price(state.symbol) > float(maxprice) + state.vars.ticks2reset:
|
||||
print("ujelo to vice nez o " + str(state.vars.ticks2reset) + ", rusime limit buye")
|
||||
##TODO toto nejak vymyslet - duplikovat?
|
||||
res = asyncio.run(state.cancel_pending_buys())
|
||||
|
||||
|
||||
#pokud mame vylozeno a pendingbuys se vyklepou a
|
||||
# 1 vykladame idned znovu
|
||||
# vyloz()
|
||||
# 2 nebo - počkat zase na signál a pokračovat dál
|
||||
# state.vars.blockbuy = 0
|
||||
# state.vars.jevylozeno = 0
|
||||
# 3 nebo - počkat na signál s enablovaným lastbuy indexem (tzn. počká nutně ještě pár barů)
|
||||
#podle BT vyhodnejsi vylozit ihned
|
||||
if len(state.vars.pendingbuys) == 0:
|
||||
state.vars.blockbuy = 0
|
||||
state.vars.jevylozeno = 0
|
||||
|
||||
#TODO toto dodelat
|
||||
#pokud je vylozeno a mame pozice a neexistuje limitka - pak ji vytvorim
|
||||
# if int(state.oe.poz)>0 and state.oe.limitka == 0:
|
||||
# #pro jistotu updatujeme pozice
|
||||
# state.oe.avgp, state.oe.poz = state.oe.pos()
|
||||
# if int(state.oe.poz) > 0:
|
||||
# cena = round(float(state.oe.avgp) + float(state.oe.stratvars["profit"]),2)
|
||||
# print("BUGF: limitka neni vytvarime, a to za cenu",cena,"mnozstvi",state.oe.poz)
|
||||
# print("aktuzalni ltp",ltp.price[state.oe.symbol])
|
||||
|
||||
# try:
|
||||
# state.oe.limitka = state.oe.sell_noasync(cena, state.oe.poz)
|
||||
# print("vytvorena limitka", state.oe.limitka)
|
||||
# except Exception as e:
|
||||
# print("Neslo vytvorit profitku. Problem,ale jedeme dal",str(e))
|
||||
# pass
|
||||
# ##raise Exception(e)
|
||||
|
||||
print(10*"*","NEXT STOP",10*"*")
|
||||
|
||||
def init(state: StrategyState):
|
||||
#place to declare new vars
|
||||
print("INIT v main",state.name)
|
||||
state.indicators['ema'] = []
|
||||
|
||||
def main():
|
||||
|
||||
try:
|
||||
strat_settings = tomli.loads("]] this is invalid TOML [[")
|
||||
except tomli.TOMLDecodeError:
|
||||
print("Yep, definitely not valid.")
|
||||
|
||||
#strat_settings = dict_replace_value(strat_settings, "None", None)
|
||||
|
||||
name = os.path.basename(__file__)
|
||||
se = Event()
|
||||
pe = Event()
|
||||
s = StrategyOrderLimitVykladaci(name = name, symbol = "BAC", account=Account.ACCOUNT2, next=next, init=init, stratvars=stratvars, open_rush=40, close_rush=0, pe=pe, se=se)
|
||||
s.set_mode(mode = Mode.PAPER,
|
||||
debug = False,
|
||||
start = datetime(2023, 3, 30, 9, 30, 0, 0, tzinfo=zoneNY),
|
||||
end = datetime(2023, 3, 31, 16, 0, 0, 0, tzinfo=zoneNY),
|
||||
cash=100000)
|
||||
|
||||
#na sekundovem baru nezaokrouhlovat MAcko
|
||||
s.add_data(symbol="BAC",rectype=RecordType.BAR,timeframe=5,minsize=100,update_ltp=True,align=StartBarAlign.ROUND,mintick=0, exthours=True)
|
||||
#s.add_data(symbol="C",rectype=RecordType.BAR,timeframe=1,filters=None,update_ltp=True,align=StartBarAlign.ROUND,mintick=0)
|
||||
|
||||
s.start()
|
||||
print("zastavujeme")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
103
v2realbot/ENTRY_backtest_strategyWatched.py
Normal file
103
v2realbot/ENTRY_backtest_strategyWatched.py
Normal file
@ -0,0 +1,103 @@
|
||||
from strategy.base import Strategy, StrategyState
|
||||
from strategy.strategyOrderLimitWatched import StrategyOrderLimitWatched
|
||||
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode
|
||||
from indicators import ema
|
||||
from rich import print
|
||||
from utils.utils import ltp, isrising, isfalling,trunc,AttributeDict, zoneNY
|
||||
from datetime import datetime
|
||||
from icecream import install, ic
|
||||
install()
|
||||
ic.configureOutput(includeContext=True)
|
||||
#ic.disable()
|
||||
""""
|
||||
Simple strategie pro test backtesting
|
||||
"""
|
||||
|
||||
def next(data, state: StrategyState):
|
||||
print(10*"*","NEXT START",10*"*")
|
||||
ic(state.avgp, state.positions)
|
||||
ic(state.vars.lastbuyindex)
|
||||
ic(data)
|
||||
ic(state.positions)
|
||||
ic(state.vars.watched)
|
||||
ic(state.vars.wait)
|
||||
|
||||
try:
|
||||
state.indicators.ema = ema(state.bars.hlcc4, state.vars.MA) #state.bars.vwap
|
||||
#trochu prasarna, EMAcko trunc na 3 mista - kdyz se osvedci, tak udelat efektivne
|
||||
state.indicators.ema = [trunc(i,3) for i in state.indicators.ema]
|
||||
ic(state.vars.MA, state.vars.Trend, state.indicators.ema[-5:])
|
||||
except Exception as e:
|
||||
print("No data for MA yet", str(e))
|
||||
|
||||
print("is falling",isfalling(state.indicators.ema,state.vars.Trend))
|
||||
print("is rising",isrising(state.indicators.ema,state.vars.Trend))
|
||||
|
||||
#ZDE JSEM SKONCIL
|
||||
#nejprve zacit s BARy
|
||||
|
||||
#TODO vyzkoušet limit buy - vetsina z nakupu by se dala koupit o cent dva mene
|
||||
#proto dodělat LTP pro BT, neco jako get_last_price(self.state.time)
|
||||
|
||||
|
||||
##TODO vyzkouset hlidat si sell objednavku sam na zaklade tradu
|
||||
# v pripade ze to jde nahoru(is rising - nebo jiny indikator) tak neprodavat
|
||||
#vyuzit CBARy k tomuto .....
|
||||
#triggerovat buy treba po polovine CBARu, kdyz se cena bude rovnat nebo bude nizsi nez low
|
||||
#a hned na to (po potvrzeni) hlidat sell +0.01 nebo kdyz roste nechat rust.vyzkouset na LIVE
|
||||
|
||||
datetime.fromtimestamp(state.last_trade_time)
|
||||
casbaru = datetime.fromtimestamp(state.last_trade_time)-data['time']
|
||||
kupuj = casbaru.seconds > int(int(data['resolution']) * 0.4)
|
||||
ic(kupuj)
|
||||
ic(casbaru.seconds)
|
||||
|
||||
#kupujeme kdyz v druhe polovine baru je aktualni cena=low (nejnizsi)
|
||||
#isrising(state.indicators.ema,state.vars.Trend)
|
||||
#kdyz se v jednom baru pohneme o 2
|
||||
if kupuj and data['confirmed'] != 1 and data['close'] == data['low'] and float(data['close']) + 0.01 < data['open'] and state.vars.wait is False and state.vars.watched is None:
|
||||
print("BUY MARKET")
|
||||
ic(data['updated'])
|
||||
ic(state.time)
|
||||
##updatneme realnou cenou po fillu
|
||||
state.buy()
|
||||
state.vars.wait = True
|
||||
|
||||
if state.vars.watched and state.vars.wait is False:
|
||||
currprice = state.interface.get_last_price(symbol = state.symbol)
|
||||
ic(currprice)
|
||||
if float(currprice) > (float(state.vars.watched) + float(state.vars.profit)):
|
||||
ic(state.time)
|
||||
ic("prodavame", currprice)
|
||||
print("PRODAVAME")
|
||||
##vymyslet jak odchytavat obecne chyby a vracet cislo objednavky
|
||||
state.interface.sell(size=1)
|
||||
state.vars.wait = True
|
||||
|
||||
|
||||
print(10*"*","NEXT STOP",10*"*")
|
||||
|
||||
def init(state: StrategyState):
|
||||
#place to declare new vars
|
||||
print("INIT v main",state.name)
|
||||
state.vars['novaprom'] = 4
|
||||
state.indicators['ema'] = []
|
||||
|
||||
def main():
|
||||
stratvars = AttributeDict(maxpozic = 1, chunk = 1, MA = 2, Trend = 2, profit = 0.005, lastbuyindex=-6, pendingbuys={},watched = None, wait = False)
|
||||
|
||||
s = StrategyOrderLimitWatched(name = "BackTEST", symbol = "BAC", next=next, init=init, stratvars=stratvars, debug=False)
|
||||
s.set_mode(mode = Mode.PAPER,
|
||||
start = datetime(2023, 3, 24, 11, 30, 0, 0, tzinfo=zoneNY),
|
||||
end = datetime(2023, 3, 24, 11, 45, 0, 0, tzinfo=zoneNY),
|
||||
cash=100000)
|
||||
|
||||
#na sekundovem baru nezaokrouhlovat MAcko
|
||||
s.add_data(symbol="BAC",rectype=RecordType.CBAR,timeframe=5,filters=None,update_ltp=True,align=StartBarAlign.ROUND,mintick=0)
|
||||
#s.add_data(symbol="C",rectype=RecordType.BAR,timeframe=1,filters=None,update_ltp=True,align=StartBarAlign.ROUND,mintick=0)
|
||||
|
||||
s.start()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
70
v2realbot/ENTRY_latency_roundtrip.py
Normal file
70
v2realbot/ENTRY_latency_roundtrip.py
Normal file
@ -0,0 +1,70 @@
|
||||
from strategy.base import Strategy
|
||||
from strategy.base import StrategyState
|
||||
from enums import RecordType, StartBarAlign, Mode
|
||||
from config import API_KEY, SECRET_KEY, MAX_BATCH_SIZE, PAPER
|
||||
from indicators import ema
|
||||
from rich import print
|
||||
from utils import ltp, isrising, isfalling,trunc,AttributeDict
|
||||
from datetime import datetime
|
||||
|
||||
""""
|
||||
Simple strategie pro měření roundtripu na konkrétním prostředí
|
||||
|
||||
- koupí 1 akcii a vypíše časy
|
||||
- tradu který triggeroval
|
||||
- čas triggeru buy
|
||||
- příchod zpětné notifikace NEW
|
||||
- příchod zpětné notifikace FILL
|
||||
- vypíše trade, když přijde do agregátoru (vyžaduje do agregátoru na řádek 79: if int(data['s']) == 1: print(data))
|
||||
- vyžaduje ve strategy base v orderupdate
|
||||
- print("NOTIFICATION ARRIVED AT:", datetime.now().timestamp(), datetime.now())
|
||||
- print(data)
|
||||
|
||||
výsledek latencyroudntrip.log
|
||||
|
||||
"""
|
||||
|
||||
def next(data, state: StrategyState):
|
||||
print("avgp:", state.avgp)
|
||||
print("positions", state.positions)
|
||||
print("přišly tyto data", data)
|
||||
print("bar updated time:", data['updated'], datetime.fromtimestamp(data['updated']))
|
||||
print("state time(now):", state.time, datetime.fromtimestamp(state.time))
|
||||
#print("trades history", state.trades)
|
||||
#print("bar history", state.bars)
|
||||
print("ltp", ltp.price["BAC"], ltp.time["BAC"])
|
||||
|
||||
try:
|
||||
ema_output = ema(state.bars.hlcc4, state.vars.MA) #state.bars.vwap
|
||||
ema_output = [trunc(i,3) for i in ema_output]
|
||||
print("emacko na wvap",state.vars.MA,":", ema_output[-5:])
|
||||
except:
|
||||
print("No data for MA yet")
|
||||
|
||||
print("MA is falling",state.vars.Trend,"value:",isfalling(ema_output,state.vars.Trend))
|
||||
print("MA is rising",state.vars.Trend,"value:",isrising(ema_output,state.vars.Trend))
|
||||
|
||||
if isfalling(ema_output,state.vars.Trend) and state.vars.blockbuy == 0:
|
||||
print("kupujeme MARKET")
|
||||
print("v baru mame cas posledniho tradu", data['updated'])
|
||||
print("na LIVE je skutecny cas - tento ", state.time)
|
||||
print("v nem odesilame")
|
||||
state.interface.buy(time=state.time)
|
||||
state.vars.blockbuy = 1
|
||||
|
||||
def init(state: StrategyState):
|
||||
print("INIT strategie", state.name, "symbol", state.symbol)
|
||||
|
||||
def main():
|
||||
stratvars = AttributeDict(maxpozic = 200, chunk = 10, MA = 3, Trend = 3, profit = 0.01, blockbuy=0, lastbuyindex=0, pendingbuys={})
|
||||
s = Strategy(name = "BackTEST", symbol = "BAC", next=next, init=init, stratvars=stratvars)
|
||||
|
||||
#s.set_mode(mode = Mode.BT, start= datetime(2023, 3, 16, 15, 54, 30, 0), end=datetime(2023, 3, 16, 15, 54, 40, 999999))
|
||||
|
||||
s.add_data(symbol="BAC",rectype=RecordType.BAR,timeframe=5,filters=None,update_ltp=True,align=StartBarAlign.ROUND,mintick=0)
|
||||
#s.add_data(symbol="C",rectype=RecordType.BAR,timeframe=1,filters=None,update_ltp=True,align=StartBarAlign.ROUND,mintick=0)
|
||||
|
||||
s.start()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
5
v2realbot/ENTRY_strategy_vykladaci_REFACTOR.py
Normal file
5
v2realbot/ENTRY_strategy_vykladaci_REFACTOR.py
Normal file
@ -0,0 +1,5 @@
|
||||
##pro refactor vykladaci strategie: tipy:
|
||||
|
||||
##pri CANCEL pendindbuys - lokalni pending buys zrušit hnedka po synchronním odeslání, nemusim cekat na potvrzeni
|
||||
##ve zpetne notifikaci FILLU - je uvedena aktuální počet pozice - tzn. nemusím volat POS, ale jen si je dotahnu odsud. Pozor avgp tu neni.
|
||||
|
||||
200
v2realbot/FINAL_MA_VYK_10s_AGGR_mastrategyvykladaci.py
Normal file
200
v2realbot/FINAL_MA_VYK_10s_AGGR_mastrategyvykladaci.py
Normal file
@ -0,0 +1,200 @@
|
||||
from strategy import MyStrategy, StrategyState, Strategy
|
||||
from enums import RecordType, StartBarAlign
|
||||
from config import API_KEY, SECRET_KEY, MAX_BATCH_SIZE, PAPER
|
||||
from indicators import ema
|
||||
from rich import print
|
||||
from utils import ltp, isrising, isfalling,trunc
|
||||
|
||||
|
||||
|
||||
""""
|
||||
TBD - zpomalit - nekupovat okamzite nechat dychat
|
||||
|
||||
|
||||
MA Vykládcí Strategie s LIMIT BUY
|
||||
# aktualni nastaveni - VELMI AGRESIVNI, STALE KUPUJE, IDEALNI NA POTVRZENE RUSTOVE DNY
|
||||
- jede na 10s
|
||||
- BUY and HOLD alternative
|
||||
- dat do seznamu hotovych strategii
|
||||
|
||||
atributy:
|
||||
ticks2reset - počet ticků po kterých se resetnou prikazy pokud neni plneni
|
||||
|
||||
TODO:
|
||||
- pridat reconciliaci po kazdem X tem potvrzenem baru - konzolidace otevrenych pozic a limitek
|
||||
- do kazde asynchronni notifkace orderupdate dat ochranu, kdyz uz ten stav neplati (tzn. napr. pro potvrzeni buye se uz prodalo)
|
||||
- podchytit: kdykoliv limitka neexistuje, ale mam pozice, tak ji vytvorit (podchytit situace v rush hours)
|
||||
- cancel pendign buys - dictionary changed size during iteration podychytit. lock
|
||||
"""
|
||||
ticks2reset = 0.03
|
||||
#TODO pokud bar klesne o jeden nebo vice - tak signál - DEFENZIVNI MOD
|
||||
#TODO pouzivat tu objekt ochrana (ktery jen vlozim do kodu a kdyz vrati exceptionu tak jdeme do dalsi iterace)
|
||||
# tak zustane strategie cista
|
||||
#TODO rušit (pending buys) když oe.poz = 0 a od nejvetsi pending buys je
|
||||
# ltp.price vice nez 5 ticků
|
||||
|
||||
def next(data, state: StrategyState):
|
||||
|
||||
def vyloz(pozic: int):
|
||||
print("vykladame na pocet pozic", pozic)
|
||||
# defenzivni krivka
|
||||
curve = [0.01, 0.01, 0.01, 0.02, 0.02, 0.02, 0.02,0.03,0.03,0.03,0.03, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04]
|
||||
#cent curve = [0.01, 0.01, 0.01,0.01, 0.01, 0.01,0.01, 0.01,0.01, 0.01, 0.01, 0.01,0.01, 0.01,0.01, 0.01,0.01, 0.01,0.01, 0.01,0.01, 0.01,0.01, 0.01,0.01, 0.01]
|
||||
#defenzivnejsi s vetsimi mezerami v druhe tretine a vetsi vahou pro dokupy
|
||||
|
||||
# krivka pro AVG, tzn. exponencialne pridavame 0.00
|
||||
curve = [0.01, 0, 0.01, 0, 0, 0.01, 0, 0, 0, 0.01, 0, 0, 0, 0, 0.01, 0, 0, 0, 0, 0, 0.01, 0,0,0,0,0,0, 0.01, 0,0,0,0,0,0,0,0,0.01,0,0,0,0,0,0,0,0,0, 0.01,0,0,0,0,0,0,0,0,0,0.01, 0,0,0,0,0,00,0,0,0, 0.5, 0,0,0,0,0.5,0,0,0]
|
||||
|
||||
# 10ty clen je o 0.05 tzn.triggeruje nam to tick2reset
|
||||
#curve = [0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.01, 0.02, 0.02, 0.05, 0.01, 0.04, 0.01, 0.01, 0.04, 0.05, 0.03, 0.05, 0.01, 0.03, 0.01,0.01, 0.04, 0.01, 0.05,0.01, 0.01,0.01, 0.01]
|
||||
|
||||
#defenzivni krivku nastavime vetsimi mezerami a v nich 0.01 - tim se prida vetsi mnostvi a vic se naredi
|
||||
# 0.04, 0.01,
|
||||
|
||||
#curve = [0.01,0.01, 0.01, 0.02, 0.02, 0.02, 0.02]
|
||||
#cena pro prvni objednavky
|
||||
price = trunc(float(ltp.price[state.oe.symbol]),2)
|
||||
print("aktualni cena pri vykladu - pro prvni", price)
|
||||
qty = int(state.variables.chunk)
|
||||
last_price = price
|
||||
if len(curve) < pozic:
|
||||
pozic = len(curve)
|
||||
#stejné ceny posilame jako jednu objednávku
|
||||
for i in range(0,pozic):
|
||||
price = round(float(price - curve[i]),2)
|
||||
if price == last_price:
|
||||
qty = qty + int(state.variables.chunk)
|
||||
else:
|
||||
#flush last_price and stored qty
|
||||
# OPT: pokud bude kupovat prilis brzy, osvedcila se prvni cena -0.01 (tzn. stavi prehodit last_price za price)
|
||||
state.oe.buy_l(price=last_price, size=qty, force=1)
|
||||
print(i,"BUY limitka - delta",curve[i]," cena:", price, "mnozstvi:", qty)
|
||||
qty = int(state.variables.chunk)
|
||||
|
||||
last_price = price
|
||||
|
||||
#TODO pokud cena stejna jako predchozi, tak navys predchozi - abychom nemeli vice objednavek na stejne cene (zbytecne)
|
||||
|
||||
|
||||
|
||||
|
||||
print("pending buys", state.oe.stratvars['pendingbuys'])
|
||||
print("je vylozeno",state.oe.stratvars['jevylozeno'])
|
||||
print("avg,poz,limitka",state.oe.avgp, state.oe.poz, state.oe.limitka)
|
||||
print("last buy price", state.oe.lastbuy)
|
||||
#CBAR protection, only 1x order per CBAR - then wait until another confirmed bar
|
||||
if state.variables.blockbuy == 1:
|
||||
if state.bars.confirmed[-1] == 0:
|
||||
print("OCHR: multibuy protection. waiting for next bar")
|
||||
return 0
|
||||
# pop potvrzeni jeste jednou vratime (aby se nekoupilo znova, je stale ten stejny bar)
|
||||
# a pak dalsi vejde az po minticku
|
||||
else:
|
||||
# pro vykladaci
|
||||
state.variables.blockbuy = 0
|
||||
return 0
|
||||
|
||||
|
||||
#print(state.bars) .
|
||||
# print("next")
|
||||
# print(data)
|
||||
|
||||
#TODO zkusit hlcc4
|
||||
try:
|
||||
ema_output = ema(state.bars.vwap, state.variables.MA)
|
||||
#trochu prasarna, EMAcko trunc na 3 mista - kdyz se osvedci, tak udelat efektivne
|
||||
ema_output = [trunc(i,3) for i in ema_output]
|
||||
print("emacko na wvap",state.variables.MA,":", ema_output[-5:])
|
||||
except:
|
||||
print("No data for MA yet")
|
||||
|
||||
print("MA is falling",state.variables.Trend,"value:",isfalling(ema_output,state.variables.Trend))
|
||||
print("MA is rising",state.variables.Trend,"value:",isrising(ema_output,state.variables.Trend))
|
||||
|
||||
## testuje aktualni cenu od nejvyssi visici limitky
|
||||
##toto spoustet jednou za X iterací - ted to jede pokazdé
|
||||
#pokud to ujede o vic, rusime limitky
|
||||
#TODO: zvazit jestli nechat i pri otevrenych pozicich, zatim nechavame
|
||||
#TODO int(int(state.oa.poz)/int(state.variables.chunk)) > X
|
||||
if state.oe.stratvars['jevylozeno'] == 1: # and int(state.oe.poz) == 0:
|
||||
#pokud mame vylozeno a cena je vetsi nez 0.04
|
||||
if len(state.oe.stratvars['pendingbuys'])>0:
|
||||
a = max(state.oe.stratvars['pendingbuys'].values())
|
||||
print("max cena v orderbuys", a)
|
||||
if float(ltp.price[state.oe.symbol]) > float(a) + ticks2reset:
|
||||
print("ujelo to vice nez o 4, rusime limit buye")
|
||||
state.oe.cancel_pending_buys()
|
||||
state.oe.stratvars['jevylozeno'] = 0
|
||||
|
||||
#pokud je vylozeno a mame pozice a neexistuje limitka - pak ji vytvorim
|
||||
if int(state.oe.poz)>0 and state.oe.limitka == 0:
|
||||
#pro jistotu updatujeme pozice
|
||||
state.oe.avgp, state.oe.poz = state.oe.pos()
|
||||
if int(state.oe.poz) > 0:
|
||||
cena = round(float(state.oe.avgp) + float(state.oe.stratvars["profit"]),2)
|
||||
print("BUGF: limitka neni vytvarime, a to za cenu",cena,"mnozstvi",state.oe.poz)
|
||||
print("aktuzalni ltp",ltp.price[state.oe.symbol])
|
||||
|
||||
try:
|
||||
state.oe.limitka = state.oe.sell_noasync(cena, state.oe.poz)
|
||||
print("vytvorena limitka", state.oe.limitka)
|
||||
except Exception as e:
|
||||
print("Neslo vytvorit profitku. Problem,ale jedeme dal",str(e))
|
||||
pass
|
||||
##raise Exception(e)
|
||||
|
||||
if state.oe.stratvars['jevylozeno'] == 0:
|
||||
print("neni vylozeno. Muzeme nakupovat")
|
||||
# testuji variantu, ze kupuji okamzite, nehlede na vyvoj
|
||||
if isfalling(ema_output,state.variables.Trend): # or 1==1:
|
||||
## vyloz pro pocet pozic (maximalni minus aktualni)
|
||||
#kolik nam zbyva pozic
|
||||
|
||||
#HOKUS: vykladame pouze polovinu pozic - dalsi polovinu davame v dalsimrunu
|
||||
# a = available pozice a/2
|
||||
a = int(int(state.variables.maxpozic)/int(state.variables.chunk)-(int(state.oe.poz)/int(state.variables.chunk)))
|
||||
a = int(a)
|
||||
print("Vykladame na pocet pozic", a)
|
||||
|
||||
|
||||
vyloz(pozic=a)
|
||||
#blokneme nakupy na dalsi bar
|
||||
state.variables.blockbuy = 1
|
||||
state.oe.stratvars['jevylozeno'] = 1
|
||||
|
||||
|
||||
#ulozime id baru
|
||||
# state.variables.lastbuyindex = state.bars.index[-1]
|
||||
|
||||
# je vylozeno
|
||||
else:
|
||||
## po kazde 4te pozici delame revykladani na aktualni zbytek pozic
|
||||
if (int(state.oe.poz)/(int(state.variables.chunk)) % 4 == 0):
|
||||
print("ctvrta pozice - v budoucnu realignujeme")
|
||||
# state.oe.cancel_pending_buys()
|
||||
# ##smazeme ihned pole - necekame na notifiaci
|
||||
# vyloz((int(state.variables.maxpozic)-int(state.oe.poz)/state.variables.chunk))
|
||||
|
||||
|
||||
#kazdy potvrzeny bar dotahneme pro jistotu otevřené objednávky a nahradíme si stav aktuálním
|
||||
|
||||
#pro jistotu update pozic - kdyz se nic nedeje nedeje
|
||||
#pripadne dat pryc
|
||||
#print("upatujeme pozice")
|
||||
#state.oe.avgp, state.oe.poz = state.oe.pos()
|
||||
|
||||
def init(state: StrategyState):
|
||||
print("init - zatim bez data")
|
||||
print(state.oe.symbol)
|
||||
print(state.oe.pos())
|
||||
print()
|
||||
|
||||
def main():
|
||||
stratvars = dict(maxpozic = 2000, chunk = 20, MA = 3, Trend = 4, profit = 0.01, blockbuy=0, lastbuyindex=0, pendingbuys={}, jevylozeno=0)
|
||||
s = MyStrategy("BAC",paper=PAPER, next=next, init=init, stratvars=stratvars)
|
||||
s.add_data(symbol="BAC",rectype=RecordType.CBAR,timeframe=12,filters=None,update_ltp=True,align=StartBarAlign.ROUND,mintick=4)
|
||||
|
||||
s.start()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
0
v2realbot/__init__.py
Normal file
0
v2realbot/__init__.py
Normal file
BIN
v2realbot/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
v2realbot/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
v2realbot/__pycache__/config.cpython-310.pyc
Normal file
BIN
v2realbot/__pycache__/config.cpython-310.pyc
Normal file
Binary file not shown.
0
v2realbot/backtesting/__init__.py
Normal file
0
v2realbot/backtesting/__init__.py
Normal file
BIN
v2realbot/backtesting/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
v2realbot/backtesting/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
v2realbot/backtesting/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
v2realbot/backtesting/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
v2realbot/backtesting/__pycache__/backtester.cpython-310.pyc
Normal file
BIN
v2realbot/backtesting/__pycache__/backtester.cpython-310.pyc
Normal file
Binary file not shown.
BIN
v2realbot/backtesting/__pycache__/backtester.cpython-311.pyc
Normal file
BIN
v2realbot/backtesting/__pycache__/backtester.cpython-311.pyc
Normal file
Binary file not shown.
816
v2realbot/backtesting/backtester.py
Normal file
816
v2realbot/backtesting/backtester.py
Normal file
@ -0,0 +1,816 @@
|
||||
#import os,sys
|
||||
#sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
"""
|
||||
Backtester component, allows to:
|
||||
|
||||
pro lepší trasovatelnost máme následující razítka
|
||||
open orders
|
||||
- submitted_at
|
||||
trades
|
||||
- submitted_at
|
||||
- filled_at
|
||||
|
||||
execute_orders_and_callbacks(time)
|
||||
- called before iteration
|
||||
- execute open orders before "time" and calls notification callbacks
|
||||
|
||||
market buy
|
||||
limit buy
|
||||
market sell
|
||||
limit sell
|
||||
cancel order by id
|
||||
replace order by id
|
||||
get positions
|
||||
|
||||
STATUSES supported:
|
||||
- FILLED (including callbacks)
|
||||
|
||||
not supported: NEW, ACCEPTED, CANCELED (currently no callback action will be backtestable on those)
|
||||
- případné nad těmito lze dát do synchronní části (api vrací rovnou zda objednávka neexistuje, pokud existuje tak předpokládáme vyplnění)
|
||||
|
||||
supports standard validations and blocking of share and cash upon order submit
|
||||
supports only GTC order validity
|
||||
no partial fills
|
||||
RETURN
|
||||
1 - ok
|
||||
0 - ok, noaction
|
||||
- 1 - error
|
||||
|
||||
"""
|
||||
from uuid import UUID, uuid4
|
||||
from alpaca.trading.enums import OrderSide, OrderStatus, TradeEvent, OrderType
|
||||
from v2realbot.common.model import TradeUpdate, Order
|
||||
#from rich import print
|
||||
import threading
|
||||
import asyncio
|
||||
from v2realbot.config import BT_DELAYS
|
||||
from v2realbot.utils.utils import AttributeDict, ltp, zoneNY, trunc, count_decimals,print
|
||||
from v2realbot.utils.tlog import tlog
|
||||
from datetime import datetime, timedelta
|
||||
import pandas as pd
|
||||
#import matplotlib.pyplot as plt
|
||||
#import seaborn; seaborn.set()
|
||||
#import mplfinance as mpf
|
||||
import plotly.graph_objects as go
|
||||
from plotly.subplots import make_subplots
|
||||
import numpy as np
|
||||
from bisect import bisect_left
|
||||
from v2realbot.utils.dash_save_html import make_static
|
||||
import dash_bootstrap_components as dbc
|
||||
from dash.dependencies import Input, Output
|
||||
from dash import dcc, html, dash_table, Dash
|
||||
from config import DATA_DIR
|
||||
""""
|
||||
LATENCY DELAYS
|
||||
.000 trigger - last_trade_time (.4246266)
|
||||
+.020 vstup do strategie a BUY (.444606)
|
||||
+.023 submitted (.469198)
|
||||
+.008 filled (.476695552)
|
||||
+.023 fill not(.499888)
|
||||
"""
|
||||
lock = threading.Lock
|
||||
|
||||
#todo nejspis dat do classes, aby se mohlo backtestovat paralelne
|
||||
#ted je globalni promena last_time_now a self.account a cash
|
||||
class Backtester:
|
||||
def __init__(self, symbol: str, order_fill_callback: callable, btdata: list, bp_from: datetime, bp_to: datetime, cash: float = 100000):
|
||||
#this TIME value determines true time for submit, replace, cancel order should happen (allowing past)
|
||||
#it is set by every iteration of BT or before fill callback - allowing past events to happen
|
||||
self.time = None
|
||||
self.symbol = symbol
|
||||
self.order_fill_callback = order_fill_callback
|
||||
self.btdata = btdata
|
||||
self.backtest_start = None
|
||||
self.backtest_end = None
|
||||
self.cash_init = cash
|
||||
#backtesting period
|
||||
self.bp_from = bp_from
|
||||
self.bp_to = bp_to
|
||||
self.cash = cash
|
||||
self.trades = []
|
||||
self.account = { "BAC": [0, 0] }
|
||||
# { "BAC": [avgp, size] }
|
||||
self.open_orders =[]
|
||||
# self.open_orders = [Order(id=uuid4(),
|
||||
# submitted_at = datetime(2023, 3, 17, 9, 30, 0, 0, tzinfo=zoneNY),
|
||||
# symbol = "BAC",
|
||||
# qty = 1,
|
||||
# status = OrderStatus.ACCEPTED,
|
||||
# order_type = OrderType.LIMIT,
|
||||
# side = OrderSide.BUY,
|
||||
# limit_price=22.4),
|
||||
# Order(id=uuid4(),
|
||||
# submitted_at = datetime(2023, 3, 17, 9, 30, 00, 0, tzinfo=zoneNY),
|
||||
# symbol = "BAC",
|
||||
# qty = 1,
|
||||
# order_type = OrderType.MARKET,
|
||||
# status = OrderStatus.ACCEPTED,
|
||||
# side = OrderSide.BUY)]
|
||||
|
||||
#
|
||||
def execute_orders_and_callbacks(self, intime: float):
|
||||
"""""
|
||||
Voláno ze strategie před každou iterací s časem T.
|
||||
Provede exekuci otevřených objednávek, které by se v reálu vyplnili do tohoto času.
|
||||
Pro vyplněné posílá fill notifikaci a volá callback s časem FILLu.
|
||||
|
||||
- current time - state.time
|
||||
- actual prices - self.btdata
|
||||
- callback after order fill - self.order_fill_callback
|
||||
- set time for order fill callback - self.time
|
||||
"""""
|
||||
|
||||
print(10*"*",intime,"Exec OPEN ORDERS ",len(self.open_orders)," time", datetime.fromtimestamp(intime).astimezone(zoneNY),10*"*")
|
||||
# print("cash before", cash)
|
||||
# print("BT: executing pending orders")
|
||||
# print("list before execution", self.open_orders)
|
||||
|
||||
if len(self.open_orders) == 0:
|
||||
ic("no pend orders")
|
||||
return 0
|
||||
|
||||
changes = 0
|
||||
|
||||
#iterating while removing items - have to use todel list
|
||||
todel = []
|
||||
#with lock:
|
||||
"""
|
||||
Pripravime si pracovni list
|
||||
btdata obsahuje vsechny aktualni timestampy tradu a jejich cenu.
|
||||
1) pracujeme vzdy OD zacatku listu DO indexu odpovidajici aktualnimu casu
|
||||
2) zjistime si index a pak iterujeme nad nim
|
||||
3) po skonceni pak tento pracovni kus umazeme
|
||||
"""
|
||||
|
||||
"""
|
||||
Assumes myList is sorted. Returns first biggeer value to the number.
|
||||
"""
|
||||
index_end = bisect_left(self.btdata, (intime,))
|
||||
|
||||
# #find index of state.time in btdata - nahrazeno BISECT
|
||||
# for index_end in range(len(self.btdata)):
|
||||
# #print(btdata[i][0])
|
||||
# #print(i)
|
||||
# if self.btdata[index_end][0] >= intime:
|
||||
# break
|
||||
|
||||
#pracovni list
|
||||
ic("work_range 0:index_end")
|
||||
ic(index_end)
|
||||
work_range = self.btdata[0:index_end]
|
||||
ic(len(work_range))
|
||||
#print("index_end", i)
|
||||
#print("oriznuto",self.btdata[0:i+1])
|
||||
|
||||
for order in self.open_orders:
|
||||
#pokud je vyplneny symbol, tak jedeme jen tyto, jinak vsechny
|
||||
print(order.id, datetime.timestamp(order.submitted_at), order.symbol, order.side, order.order_type, order.qty, order.limit_price, order.submitted_at)
|
||||
if order.canceled_at:
|
||||
ic("deleting canceled order",order.id)
|
||||
todel.append(order)
|
||||
elif not self.symbol or order.symbol == self.symbol:
|
||||
#pricteme mininimalni latency od submittu k fillu
|
||||
if order.submitted_at.timestamp() + BT_DELAYS.sub_to_fill > float(intime):
|
||||
ic("too soon for",order.id)
|
||||
#try to execute
|
||||
else:
|
||||
#try to execute specific order
|
||||
a = self._execute_order(o = order, intime=intime, work_range=work_range)
|
||||
if a == 1:
|
||||
ic("EXECUTED")
|
||||
todel.append(order)
|
||||
changes = 1
|
||||
else:
|
||||
ic("NOT EXECUTED",a)
|
||||
#ic("istodel",todel)
|
||||
#vymazu z pending orderu vschny zprocesovane nebo ty na výmaz
|
||||
for i in todel:
|
||||
self.open_orders.remove(i)
|
||||
todel = {}
|
||||
|
||||
#tady udelat pripadny ořez self.btdata - priste zaciname od zacatku
|
||||
#ic("before delete", len(self.btdata))
|
||||
|
||||
#TEST zkusime to nemazat, jak ovlivni performance
|
||||
#Mazeme, jinak je to hruza
|
||||
del self.btdata[0:index_end-2]
|
||||
#ic("after delete",len(self.btdata[0:index_end]))
|
||||
|
||||
if changes: return 1
|
||||
else: return 0
|
||||
# print("pending orders after execution", self.open_orders)
|
||||
# print("trades after execution", trades)
|
||||
# print("self.account after execution", self.account)
|
||||
# print("cash after", cash)
|
||||
|
||||
def _execute_order(self, o: Order, intime: float, work_range):
|
||||
"""tries to execute order
|
||||
|
||||
o - specific Order
|
||||
time - intime
|
||||
work_range - aktualni slice of btdata pro tuto iteraci [(timestamp,price)] """
|
||||
|
||||
fill_time = None
|
||||
fill_price = None
|
||||
order_min_fill_time = o.submitted_at.timestamp() + BT_DELAYS.sub_to_fill
|
||||
ic(order_min_fill_time)
|
||||
ic(len(work_range))
|
||||
|
||||
if o.order_type == OrderType.LIMIT:
|
||||
if o.side == OrderSide.BUY:
|
||||
for i in 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:
|
||||
#(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])
|
||||
break
|
||||
else:
|
||||
for i in work_range:
|
||||
#print(i)
|
||||
if float(i[0]) > float(order_min_fill_time+BT_DELAYS.limit_order_offset) and i[1] >= o.limit_price:
|
||||
#(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])
|
||||
break
|
||||
|
||||
elif o.order_type == OrderType.MARKET:
|
||||
for i in work_range:
|
||||
#print(i)
|
||||
#najde prvni nejvetsi čas vetsi nez minfill
|
||||
if i[0] > float(order_min_fill_time):
|
||||
#(1679081919.381649, 27.88)
|
||||
ic(i)
|
||||
fill_time = i[0]
|
||||
fill_price = i[1]
|
||||
print("FILL ",o.side,"MARKET at", fill_time, datetime.fromtimestamp(fill_time).astimezone(zoneNY), "cena", i[1])
|
||||
break
|
||||
else:
|
||||
print("unknown order type")
|
||||
return -1
|
||||
|
||||
if not fill_time:
|
||||
ic("not FILLED")
|
||||
return 0
|
||||
else:
|
||||
|
||||
#order FILLED - update trades and account and cash
|
||||
o.filled_at = datetime.fromtimestamp(float(fill_time))
|
||||
o.filled_qty = o.qty
|
||||
o.filled_avg_price = float(fill_price)
|
||||
o.status = OrderStatus.FILLED
|
||||
|
||||
ic(o.filled_at, o.filled_avg_price)
|
||||
|
||||
a = self.update_account(o = o)
|
||||
if a < 0:
|
||||
tlog("update_account ERROR")
|
||||
return -1
|
||||
|
||||
trade = TradeUpdate(order = o,
|
||||
event = TradeEvent.FILL,
|
||||
execution_id = str(uuid4()),
|
||||
timestamp = datetime.fromtimestamp(fill_time),
|
||||
position_qty= self.account[o.symbol][0],
|
||||
price=float(fill_price),
|
||||
qty = o.qty,
|
||||
value = float(o.qty*fill_price),
|
||||
cash = self.cash,
|
||||
pos_avg_price = self.account[o.symbol][1])
|
||||
|
||||
self.trades.append(trade)
|
||||
|
||||
# do notification with callbacks
|
||||
#print("pred appendem",self.open_orders)
|
||||
self._do_notification_with_callbacks(tradeupdate=trade, time=float(fill_time))
|
||||
#print("po appendu",self.open_orders)
|
||||
#ohandlovat nejak chyby? v LIVE je to asynchronni a takze neni ohandlovano, takze jen print
|
||||
return 1
|
||||
|
||||
def _do_notification_with_callbacks(self, tradeupdate: TradeUpdate, time: float):
|
||||
|
||||
#do callbacku je třeba zpropagovat filltime čas (včetně latency pro notifikaci), aby se pripadne akce v callbacku udály s tímto časem
|
||||
self.time = time + float(BT_DELAYS.fill_to_not)
|
||||
print("current bt.time",self.time)
|
||||
#print("FILL NOTIFICATION: ", tradeupdate)
|
||||
res = asyncio.run(self.order_fill_callback(tradeupdate))
|
||||
return 0
|
||||
|
||||
def update_account(self, o: Order):
|
||||
#updatujeme self.account
|
||||
#pokud neexistuje klic v accountu vytvorime si ho
|
||||
if o.symbol not in self.account:
|
||||
# { "BAC": [size, avgp] }
|
||||
self.account[o.symbol] = [0,0]
|
||||
|
||||
if o.side == OrderSide.BUY:
|
||||
#[size, avgp]
|
||||
if (self.account[o.symbol][0] + o.qty) == 0: newavgp = 0
|
||||
else:
|
||||
newavgp = ((self.account[o.symbol][0] * self.account[o.symbol][1]) + (o.qty * o.filled_avg_price)) / (self.account[o.symbol][0] + o.qty)
|
||||
self.account[o.symbol] = [self.account[o.symbol][0]+o.qty, newavgp]
|
||||
self.cash = self.cash - (o.qty * o.filled_avg_price)
|
||||
return 1
|
||||
#sell
|
||||
elif o.side == OrderSide.SELL:
|
||||
newsize = self.account[o.symbol][0]-o.qty
|
||||
if newsize == 0: newavgp = 0
|
||||
else:
|
||||
if self.account[o.symbol][1] == 0:
|
||||
newavgp = o.filled_avg_price
|
||||
else:
|
||||
newavgp = self.account[o.symbol][1]
|
||||
self.account[o.symbol] = [newsize, newavgp]
|
||||
self.cash = float(self.cash + (o.qty * o.filled_avg_price))
|
||||
return 1
|
||||
else:
|
||||
print("neznaama side", o.side)
|
||||
return -1
|
||||
|
||||
"""""
|
||||
BACKEND PRO API
|
||||
|
||||
TODO - možná toto předělat a brát si čas z bt.time - upravit také v BacktestInterface
|
||||
BUG:
|
||||
"""""
|
||||
|
||||
def get_last_price(self, time: float, symbol: str = None):
|
||||
"""""
|
||||
returns equity price in timestamp. Assumes myList is sorted. Returns first lower value to the number.
|
||||
optimalized as bisect left
|
||||
"""""
|
||||
pos = bisect_left(self.btdata, (time,))
|
||||
ic("vracime last price")
|
||||
ic(time)
|
||||
if pos == 0:
|
||||
ic(self.btdata[0][1])
|
||||
return self.btdata[0][1]
|
||||
afterTime, afterPrice = self.btdata[pos-1]
|
||||
ic(afterPrice)
|
||||
return afterPrice
|
||||
|
||||
|
||||
#not optimalized:
|
||||
# for i in range(len(self.btdata)):
|
||||
# #print(btdata[i][0])
|
||||
# #print(i)
|
||||
# if self.btdata[i][0] >= time:
|
||||
# break
|
||||
# ic(time, self.btdata[i-1][1])
|
||||
# ic("get last price")
|
||||
# return self.btdata[i-1][1]
|
||||
|
||||
def submit_order(self, time: float, symbol: str, side: OrderSide, size: int, order_type: OrderType, price: float = None):
|
||||
"""submit order
|
||||
- zakladni validace
|
||||
- vloží do self.open_orders s daným časem
|
||||
- vrátí orderID
|
||||
|
||||
status NEW se nenotifikuje
|
||||
|
||||
TBD dotahovani aktualni ceny podle backtesteru
|
||||
"""
|
||||
print("BT: submit order entry")
|
||||
|
||||
if not time or time < 0:
|
||||
print("time musi byt vyplneny")
|
||||
return -1
|
||||
|
||||
if not size or int(size) < 0:
|
||||
print("size musi byt vetsi nez 0")
|
||||
return -1
|
||||
|
||||
if (order_type != OrderType.MARKET) and (order_type != OrderType.LIMIT):
|
||||
tlog("ordertype market and limit supported only", order_type)
|
||||
return -1
|
||||
|
||||
if not side == OrderSide.BUY and not side == OrderSide.SELL:
|
||||
print("side buy/sell required")
|
||||
return -1
|
||||
|
||||
if order_type == OrderType.LIMIT and count_decimals(price) > 2:
|
||||
print("only 2 decimals supported", price)
|
||||
return -1
|
||||
|
||||
#pokud neexistuje klic v accountu vytvorime si ho
|
||||
if symbol not in self.account:
|
||||
# { "BAC": [size, avgp] }
|
||||
self.account[symbol] = [0,0]
|
||||
|
||||
#check for available quantity
|
||||
if side == OrderSide.SELL:
|
||||
reserved = 0
|
||||
#with lock:
|
||||
for o in self.open_orders:
|
||||
if o.qty == OrderSide.SELL and o.symbol == symbol:
|
||||
reserved += o.qty
|
||||
#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)
|
||||
return -1
|
||||
|
||||
#check for available cash
|
||||
if side == OrderSide.BUY:
|
||||
reserved = 0
|
||||
#with lock:
|
||||
for o in self.open_orders:
|
||||
if o.qty == OrderSide.BUY:
|
||||
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)
|
||||
|
||||
cena = price if price else self.get_last_price(time, self.symbol)
|
||||
if (self.cash - reserved - float(int(size)*float(cena))) < 0:
|
||||
print("not enough cash. cash",self.cash,"reserved",reserved,"available",self.cash-reserved,"needed",float(int(size)*float(cena)))
|
||||
return -1
|
||||
|
||||
id = str(uuid4())
|
||||
order = Order(id=id,
|
||||
submitted_at = datetime.fromtimestamp(float(time)),
|
||||
symbol=symbol,
|
||||
order_type = order_type,
|
||||
status = OrderStatus.ACCEPTED,
|
||||
side=side,
|
||||
qty=int(size),
|
||||
limit_price=(float(price) if price else None))
|
||||
|
||||
self.open_orders.append(order)
|
||||
ic("order SUBMITTED", order)
|
||||
return id
|
||||
|
||||
|
||||
def replace_order(self, id: str, time: float, size: int = None, price: float = None):
|
||||
"""replace order
|
||||
- zakladni validace vrací synchronně
|
||||
- vrací číslo nové objednávky
|
||||
"""
|
||||
print("BT: replace order entry",id,size,price)
|
||||
|
||||
if not price and not size:
|
||||
print("size or price required")
|
||||
return -1
|
||||
|
||||
if len(self.open_orders) == 0:
|
||||
print("BT: order doesnt exist")
|
||||
return 0
|
||||
#with lock:
|
||||
for o in self.open_orders:
|
||||
print(o.id)
|
||||
if str(o.id) == str(id):
|
||||
newid = str(uuid4())
|
||||
o.id = newid
|
||||
o.submitted_at = datetime.fromtimestamp(time)
|
||||
o.qty = int((size if size else o.qty))
|
||||
o.limit_price = float(price if price else o.limit_price)
|
||||
print("order replaced")
|
||||
return newid
|
||||
print("BT: replacement order doesnt exist")
|
||||
return 0
|
||||
|
||||
def cancel_order(self, time: float, id: str):
|
||||
"""cancel order
|
||||
- základní validace vrací synchronně
|
||||
- vymaže objednávku z open orders
|
||||
- nastavuje v open orders flag zrušeno, který pak exekuce ignoruje a odstraní
|
||||
(je tak podchycen stav, kdy se cancel volá z bt callbacku a z iterovaného listu by se odstraňovalo během iterace)
|
||||
|
||||
TODO: teoreticky bych pred cancel order mohl zavolat execution, abych vyloucil, ze za 20ms zpozdeni, kdy se vola alpaca mi neprojde nejaka objednavka
|
||||
spise do budoucna
|
||||
"""
|
||||
print("BT: cancel order entry",id)
|
||||
if len(self.open_orders) == 0:
|
||||
print("BTC: order doesnt exist")
|
||||
return 0
|
||||
#with lock:
|
||||
for o in self.open_orders:
|
||||
if str(o.id) == id:
|
||||
o.canceled_at = time
|
||||
print("set as canceled in self.open_orders")
|
||||
return 1
|
||||
print("BTC: cantchange. open order doesnt exist")
|
||||
return 0
|
||||
|
||||
def get_open_position(self, symbol: str):
|
||||
"""get positions ->(avg,size)"""
|
||||
#print("BT:get open positions entry")
|
||||
try:
|
||||
return self.account[symbol][1], self.account[symbol][0]
|
||||
except:
|
||||
return (0,0)
|
||||
|
||||
def get_open_orders(self, side: OrderSide, symbol: str):
|
||||
"""get open orders ->list(OrderNotification)"""
|
||||
print("BT:get open orders entry")
|
||||
if len(self.open_orders) == 0:
|
||||
print("BTC: order doesnt exist")
|
||||
return 0
|
||||
res = []
|
||||
#with lock:
|
||||
for o in self.open_orders:
|
||||
if str(o.side) == side and o.symbol == symbol:
|
||||
res.append(o)
|
||||
return res
|
||||
|
||||
def display_backtest_result(self, state):
|
||||
"""
|
||||
Displays backtest results chart, trades and orders with option to save the result as static HTML.
|
||||
"""
|
||||
#open_orders to dataset
|
||||
oo_dict = AttributeDict(orderid=[],submitted_at=[],symbol=[],side=[],order_type=[],qty=[],limit_price=[],status=[])
|
||||
for t in self.open_orders:
|
||||
oo_dict.orderid.append(str(t.id))
|
||||
oo_dict.submitted_at.append(t.submitted_at)
|
||||
oo_dict.symbol.append(t.symbol)
|
||||
oo_dict.side.append(t.side)
|
||||
oo_dict.qty.append(t.qty)
|
||||
oo_dict.order_type.append(t.order_type)
|
||||
oo_dict.limit_price.append(t.limit_price)
|
||||
oo_dict.status.append(t.status)
|
||||
|
||||
open_orders_df = pd.DataFrame(oo_dict)
|
||||
open_orders_df = open_orders_df.set_index('submitted_at', drop=False)
|
||||
|
||||
#trades to dataset
|
||||
trade_dict = AttributeDict(orderid=[],timestamp=[],symbol=[],side=[],order_type=[],qty=[],price=[],position_qty=[],value=[],cash=[],pos_avg_price=[])
|
||||
for t in self.trades:
|
||||
trade_dict.orderid.append(str(t.order.id))
|
||||
trade_dict.timestamp.append(t.timestamp)
|
||||
trade_dict.symbol.append(t.order.symbol)
|
||||
trade_dict.side.append(t.order.side)
|
||||
trade_dict.qty.append(t.qty)
|
||||
trade_dict.price.append(t.price)
|
||||
trade_dict.position_qty.append(t.position_qty)
|
||||
trade_dict.value.append(t.value)
|
||||
trade_dict.cash.append(t.cash)
|
||||
trade_dict.order_type.append(t.order.order_type)
|
||||
trade_dict.pos_avg_price.append(t.pos_avg_price)
|
||||
|
||||
trade_df = pd.DataFrame(trade_dict)
|
||||
trade_df = trade_df.set_index('timestamp',drop=False)
|
||||
|
||||
#ohlcv dataset (TODO podporit i trady)
|
||||
hist_df = pd.DataFrame(state.bars)
|
||||
hist_df = hist_df.set_index('time', drop=False)
|
||||
|
||||
#indicators
|
||||
ind_df = pd.DataFrame(state.indicators)
|
||||
ind_df = ind_df.set_index('time', drop=False)
|
||||
#print("Indicators", ind_df)
|
||||
#print(state.indicators)
|
||||
|
||||
#KONEC přípravy dat
|
||||
fig = make_subplots(rows=2, cols=1, shared_xaxes=True, vertical_spacing=0.02, row_heights=[0.7, 0.3], specs=[[{"secondary_y": True}],[{"secondary_y": True}]])
|
||||
|
||||
|
||||
# fig.add_trace(go.Scatter(x=trade_df.index,
|
||||
# y=trade_df.cash,
|
||||
# mode="lines+text",
|
||||
# name="Value"),
|
||||
# row = 1, col=1, secondary_y=True)
|
||||
|
||||
#add_openorders
|
||||
fig.add_trace(go.Scatter(x=open_orders_df.index,
|
||||
y=open_orders_df.limit_price,
|
||||
mode="markers+text",
|
||||
name="Open orders",
|
||||
customdata=open_orders_df.orderid,
|
||||
marker=dict(size=17, color='blue', symbol='arrow-bar-down'),
|
||||
text=open_orders_df.qty),
|
||||
row = 1, col=1, secondary_y=False)
|
||||
|
||||
#add trades
|
||||
fig.add_trace(go.Scatter(x=trade_df.loc[trade_df.side==OrderSide.BUY].index,
|
||||
y=trade_df.loc[trade_df.side==OrderSide.BUY].price,
|
||||
mode="markers+text",
|
||||
name="BUY Trades",
|
||||
customdata=trade_df.loc[trade_df.side==OrderSide.BUY].orderid,
|
||||
marker=dict(size=15, color='green', symbol='arrow-up'),
|
||||
text=trade_df.loc[trade_df.side==OrderSide.BUY].position_qty),
|
||||
row = 1, col=1, secondary_y=False)
|
||||
|
||||
fig.add_trace(go.Scatter(x=trade_df.loc[trade_df.side==OrderSide.SELL].index,
|
||||
y=trade_df.loc[trade_df.side==OrderSide.SELL].price,
|
||||
mode="markers+text",
|
||||
name="SELL Trades",
|
||||
customdata=trade_df.loc[trade_df.side==OrderSide.SELL].orderid,
|
||||
marker=dict(size=15, color='red', symbol='arrow-down'),
|
||||
text=trade_df.loc[trade_df.side==OrderSide.SELL].qty),
|
||||
row = 1, col=1, secondary_y=False)
|
||||
|
||||
#add avgprices of all buy trades
|
||||
|
||||
fig.add_trace(go.Scatter(x=trade_df.loc[trade_df.side==OrderSide.BUY].index,
|
||||
y=trade_df.loc[trade_df.side==OrderSide.BUY].pos_avg_price,
|
||||
mode="markers+text",
|
||||
name="BUY Trades avg prices",
|
||||
customdata=trade_df.loc[trade_df.side==OrderSide.BUY].orderid,
|
||||
marker=dict(size=15, color='blue', symbol='diamond-wide'),
|
||||
text=trade_df.loc[trade_df.side==OrderSide.BUY].position_qty),
|
||||
row = 1, col=1, secondary_y=False)
|
||||
|
||||
#display ohlcv
|
||||
fig.add_trace(go.Candlestick(x=hist_df.index,
|
||||
open=hist_df['open'],
|
||||
high=hist_df['high'],
|
||||
low=hist_df['low'],
|
||||
close=hist_df['close'],
|
||||
name = "OHLC"),
|
||||
row = 1, col=1, secondary_y=False)
|
||||
|
||||
#addvwap
|
||||
fig.add_trace(go.Scatter(x=hist_df.index,
|
||||
y=hist_df.vwap,
|
||||
mode="lines",
|
||||
opacity=1,
|
||||
name="VWAP"
|
||||
),
|
||||
row = 1, col=1,secondary_y=False)
|
||||
|
||||
|
||||
#display indicators from history
|
||||
for col in ind_df.columns:
|
||||
fig.add_trace(go.Scatter(x=ind_df.index, y = ind_df[col], mode = 'lines', name = col),
|
||||
row = 1, col=1, secondary_y=False)
|
||||
|
||||
fig.add_trace(go.Bar(x=hist_df.index, y=hist_df.volume, showlegend=True, marker_color='#ef5350', name='Volume'), row=2,
|
||||
col=1)
|
||||
|
||||
fig.update_layout(xaxis_rangeslider_visible=False)
|
||||
#fig.update_layout(title=f'Backtesting Results '+str(Neco.vars), yaxis_title=f'Price')
|
||||
fig.update_layout(yaxis_title=f'Price')
|
||||
fig.update_yaxes(title_text=f'Price', secondary_y=False)
|
||||
fig.update_yaxes(title_text=f'Cash value', secondary_y=True)
|
||||
fig.update_yaxes(title_text=f'Volume', row=2, col=1)
|
||||
fig.update_xaxes(title_text='Date', row=2)
|
||||
|
||||
|
||||
# #remove gaps
|
||||
# alldays =set(hist_df.time[0]+timedelta(x) for x in range((hist_df.time[len(hist_df.time)-1]- hist_df.time[0]).days))
|
||||
# missing=sorted(set(alldays)-set(hist_df.time))
|
||||
|
||||
|
||||
rangebreaks=[
|
||||
# NOTE: Below values are bound (not single values), ie. hide x to y
|
||||
dict(bounds=["sat", "mon"]), # hide weekends, eg. hide sat to before mon
|
||||
dict(bounds=[22, 15.5], pattern="hour"), # hide hours outside of 9.30am-4pm
|
||||
# dict(values=["2020-12-25", "2021-01-01"]) # hide holidays (Christmas and New Year's, etc)
|
||||
]
|
||||
|
||||
fig.update_xaxes(rangebreaks=rangebreaks)
|
||||
|
||||
|
||||
##START DASH
|
||||
app = Dash(__name__)
|
||||
|
||||
## Define the title for the app
|
||||
mytitle = dcc.Markdown('# Backtesting results')
|
||||
button = html.Button('save static', id='save', n_clicks=0)
|
||||
saved = html.Span('', id='saved')
|
||||
textik1 = html.Div('''
|
||||
Strategy:''' + state.name)
|
||||
textik2 = html.Div('''
|
||||
Tested period:'''+ self.bp_from.strftime("%d/%m/%Y, %H:%M:%S") + '-' + self.bp_to.strftime("%d/%m/%Y, %H:%M:%S"))
|
||||
textik3 = html.Div('''
|
||||
Stratvars:'''+ str(state.vars))
|
||||
textik35 = html.Div('''
|
||||
Resolution:'''+ str(state.timeframe) + "s rectype:" + str(state.rectype))
|
||||
textik4 = html.Div('''
|
||||
Started at:''' + self.backtest_start.strftime("%d/%m/%Y, %H:%M:%S") + " Duration:"+str(self.backtest_end-self.backtest_start))
|
||||
textik5 = html.Div('''
|
||||
Cash start:''' + str(self.cash_init)+ " Cash final" + str(self.cash))
|
||||
textik55 = html.Div('''
|
||||
Positions:''' + str(self.account))
|
||||
textik6 = html.Div('''
|
||||
Open orders:''' + str(len(self.open_orders)))
|
||||
textik7 = html.Div('''
|
||||
Trades:''' + str(len(self.trades)))
|
||||
orders_title = dcc.Markdown('## Open orders')
|
||||
trades_title = dcc.Markdown('## Trades')
|
||||
## Define the graph
|
||||
mygraph= dcc.Graph(id = "hlavni-graf", figure=fig)
|
||||
|
||||
open_orders_table = dash_table.DataTable(
|
||||
id="orderstable",
|
||||
data=open_orders_df.to_dict('records'),
|
||||
columns=[{'id': c, 'name': c} for c in open_orders_df.columns],
|
||||
sort_action="native",
|
||||
row_selectable="single",
|
||||
column_selectable=False,
|
||||
fill_width = False,
|
||||
filter_action = "native",
|
||||
style_table={
|
||||
'height': 500,
|
||||
'overflowY': 'scroll'
|
||||
},
|
||||
style_header={
|
||||
'backgroundColor': 'lightgrey',
|
||||
'color': 'black'
|
||||
},
|
||||
style_data={
|
||||
'backgroundColor': 'white',
|
||||
'color': 'black'
|
||||
},
|
||||
style_cell={
|
||||
'overflow': 'hidden',
|
||||
'textOverflow': 'ellipsis',
|
||||
'maxWidth': 220,
|
||||
'minWidth': 5,
|
||||
'width': 5
|
||||
}
|
||||
)
|
||||
|
||||
trades_table = dash_table.DataTable(
|
||||
id="tradestable",
|
||||
data=trade_df.to_dict('records'),
|
||||
columns=[{'id': c, 'name': c} for c in trade_df.columns],
|
||||
sort_action="native",
|
||||
row_selectable="single",
|
||||
column_selectable=False,
|
||||
fill_width = False,
|
||||
filter_action = "native",
|
||||
style_table={
|
||||
'height': 500,
|
||||
'overflowY': 'scroll'
|
||||
},
|
||||
style_header={
|
||||
'backgroundColor': 'lightgrey',
|
||||
'color': 'black'
|
||||
},
|
||||
style_data={
|
||||
'backgroundColor': 'white',
|
||||
'color': 'black'
|
||||
},
|
||||
style_cell={
|
||||
'overflow': 'hidden',
|
||||
'textOverflow': 'ellipsis',
|
||||
'maxWidth': 220,
|
||||
'minWidth': 5,
|
||||
'width': 5
|
||||
}
|
||||
# page_size=15
|
||||
)
|
||||
|
||||
@app.callback(Output("tradestable", "style_data_conditional"),
|
||||
Input("hlavni-graf", "hoverData"))
|
||||
def highlight(hoverData):
|
||||
#print(hoverData)
|
||||
if hoverData is None:
|
||||
return None
|
||||
try:
|
||||
row = hoverData["points"][0]["customdata"]
|
||||
except KeyError:
|
||||
return None
|
||||
#print(row)
|
||||
|
||||
#print({"if": {"filter_query": "{{orderid}}={}".format(row)}, "backgroundColor": "lightgrey"})
|
||||
return [
|
||||
{"if": {"filter_query": "{{orderid}}={}".format(row)}, "backgroundColor": "lightgrey"}
|
||||
]
|
||||
@app.callback(Output("orderstable", "style_data_conditional"),
|
||||
Input("hlavni-graf", "hoverData"))
|
||||
def highlight(hoverData):
|
||||
#print(hoverData)
|
||||
if hoverData is None:
|
||||
return None
|
||||
try:
|
||||
row = hoverData["points"][0]["customdata"]
|
||||
except KeyError:
|
||||
return None
|
||||
#print(row)
|
||||
|
||||
#print({"if": {"filter_query": "{{orderid}}={}".format(row)}, "backgroundColor": "lightgrey"})
|
||||
return [
|
||||
{"if": {"filter_query": "{{orderid}}={}".format(row)}, "backgroundColor": "lightgrey"}
|
||||
]
|
||||
|
||||
@app.callback(
|
||||
Output('saved', 'children'),
|
||||
Input('save', 'n_clicks'),
|
||||
)
|
||||
def save_result(n_clicks):
|
||||
if n_clicks == 0:
|
||||
return 'not saved'
|
||||
else:
|
||||
bt_dir = DATADIR + "/backtestresults/" + self.symbol + self.bp_from.strftime("%d-%m-%y-%H-%M-%S") + ' ' + self.bp_to.strftime("%d-%m-%y-%H-%M-%S") + ' ' + str(datetime.now().microsecond)
|
||||
make_static(f'http://127.0.0.1:{port}/', bt_dir)
|
||||
return 'saved'
|
||||
|
||||
## Customize your layout
|
||||
app.layout = dbc.Container([mytitle,button,saved, textik1, textik2, textik3, textik35, textik4, textik5, textik55, textik6,textik7, mygraph, trades_title, trades_table, orders_title, open_orders_table])
|
||||
|
||||
port = 9050
|
||||
print("Backtest FINSIHED"+str(self.backtest_end-self.backtest_start))
|
||||
threading.Thread(target=lambda: app.run(port=port, debug=False, use_reloader=False)).start()
|
||||
#app.run_server(debug=False, port = port)
|
||||
print("tady se spouští server")
|
||||
print("Jedeme dal?")
|
||||
0
v2realbot/common/__init__.py
Normal file
0
v2realbot/common/__init__.py
Normal file
BIN
v2realbot/common/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
v2realbot/common/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
v2realbot/common/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
v2realbot/common/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
v2realbot/common/__pycache__/model.cpython-310.pyc
Normal file
BIN
v2realbot/common/__pycache__/model.cpython-310.pyc
Normal file
Binary file not shown.
BIN
v2realbot/common/__pycache__/model.cpython-311.pyc
Normal file
BIN
v2realbot/common/__pycache__/model.cpython-311.pyc
Normal file
Binary file not shown.
100
v2realbot/common/model.py
Normal file
100
v2realbot/common/model.py
Normal file
@ -0,0 +1,100 @@
|
||||
from uuid import UUID, uuid4
|
||||
from alpaca.trading.enums import OrderSide, OrderStatus, TradeEvent, OrderClass, OrderType, TimeInForce
|
||||
#from utils import AttributeDict
|
||||
from rich import print
|
||||
from typing import Any, Optional, List, Union
|
||||
from datetime import datetime, date
|
||||
from pydantic import BaseModel
|
||||
from v2realbot.enums.enums import Mode, Account
|
||||
|
||||
#tu samou variantu pak UpdateStrategyInstanceWhileRunning
|
||||
|
||||
#only those that can be changed UUID id prijde v parametru
|
||||
# @app.put("/api/v1/users/{id}")
|
||||
# async def update_user(user_update: UpdateUser, id: UUID):
|
||||
# for user in db:
|
||||
# if user.id == id:
|
||||
# if user_update.first_name is not None:
|
||||
# user.first_name = user_update.first_name
|
||||
# if user_update.last_name is not None:
|
||||
# user.last_name = user_update.last_name
|
||||
# if user_update.roles is not None:
|
||||
# user.roles = user_update.roles
|
||||
# return user.id
|
||||
# raise HTTPException(status_code=404, detail=f"Could not find user with id: {id}")
|
||||
|
||||
#persisted object in pickle
|
||||
class StrategyInstance(BaseModel):
|
||||
id: Optional[UUID | str | None] = None
|
||||
id2: int
|
||||
name: str
|
||||
symbol: str
|
||||
class_name: str
|
||||
script: str
|
||||
open_rush: int = 0
|
||||
close_rush: int = 0
|
||||
stratvars_conf: str
|
||||
add_data_conf: str
|
||||
note: Optional[str]
|
||||
history: Optional[str]
|
||||
|
||||
class RunRequest(BaseModel):
|
||||
id: UUID
|
||||
account: Account
|
||||
mode: Mode
|
||||
debug: bool = False
|
||||
bt_from: datetime = None
|
||||
bt_to: datetime = None
|
||||
cash: int = 100000
|
||||
|
||||
class RunnerView(BaseModel):
|
||||
id: UUID
|
||||
run_started: Optional[datetime] = None
|
||||
run_mode: Mode
|
||||
run_account: Account
|
||||
run_stopped: Optional[datetime] = None
|
||||
run_paused: Optional[datetime] = None
|
||||
|
||||
#Running instance - not persisted
|
||||
class Runner(BaseModel):
|
||||
id: UUID
|
||||
run_started: Optional[datetime] = None
|
||||
run_mode: Mode
|
||||
run_account: Account
|
||||
run_stopped: Optional[datetime] = None
|
||||
run_paused: Optional[datetime] = None
|
||||
run_thread: Optional[object] = None
|
||||
run_instance: Optional[object] = None
|
||||
run_pause_ev: Optional[object] = None
|
||||
run_stop_ev: Optional[object] = None
|
||||
|
||||
class Order(BaseModel):
|
||||
id: UUID
|
||||
submitted_at: datetime
|
||||
filled_at: Optional[datetime]
|
||||
canceled_at: Optional[datetime]
|
||||
symbol: str
|
||||
qty: int
|
||||
status: OrderStatus
|
||||
order_type: OrderType
|
||||
filled_qty: Optional[int]
|
||||
filled_avg_price: Optional[float]
|
||||
side: OrderSide
|
||||
limit_price: Optional[float]
|
||||
|
||||
class TradeUpdate(BaseModel):
|
||||
event: Union[TradeEvent, str]
|
||||
execution_id: Optional[UUID]
|
||||
order: Order
|
||||
timestamp: datetime
|
||||
position_qty: Optional[float]
|
||||
price: Optional[float]
|
||||
qty: Optional[float]
|
||||
value: Optional[float]
|
||||
cash: Optional[float]
|
||||
pos_avg_price: Optional[float]
|
||||
|
||||
# class Trade(BaseModel):
|
||||
# order: Order
|
||||
# value: float
|
||||
|
||||
100
v2realbot/conf.toml
Normal file
100
v2realbot/conf.toml
Normal file
@ -0,0 +1,100 @@
|
||||
[general]
|
||||
general_attributtes = true
|
||||
ping_time = 1200
|
||||
|
||||
#V1 na BAC
|
||||
[[strategies]]
|
||||
name = "V1 na BAC"
|
||||
symbol = "BAC"
|
||||
script = "ENTRY_backtest_strategyVykladaci"
|
||||
class = "StrategyOrderLimitVykladaci"
|
||||
open_rush = 0
|
||||
close_rush = 0
|
||||
|
||||
[stratvars]
|
||||
maxpozic = 200
|
||||
chunk = 10
|
||||
MA = 6
|
||||
Trend = 5
|
||||
profit = 0.02
|
||||
lastbuyindex=-6
|
||||
pendingbuys={}
|
||||
limitka = "None"
|
||||
jevylozeno=0
|
||||
vykladka=5
|
||||
curve = [0.01, 0.01, 0.01, 0.0, 0.02, 0.02, 0.01,0.01, 0.01,0.03, 0.01, 0.01, 0.01,0.04, 0.01,0.01, 0.01,0.05, 0.01,0.01, 0.01,0.01, 0.06,0.01, 0.01,0.01, 0.01]
|
||||
blockbuy = 0
|
||||
ticks2reset = 0.04
|
||||
|
||||
[[add_data]]
|
||||
symbol="BAC"
|
||||
rectype= "bar"
|
||||
timeframe=5
|
||||
update_ltp=true
|
||||
align="round"
|
||||
mintick=0
|
||||
minsize=100
|
||||
exthours=false
|
||||
|
||||
#D2 na C
|
||||
[[strategies]]
|
||||
name = "D2 na C"
|
||||
script = "ENTRY_backtest_strategyKOKA-ok"
|
||||
class = "StrategyOrderLimitKOKA"
|
||||
symbol = "C"
|
||||
open_rush = 0
|
||||
close_rush = 0
|
||||
|
||||
[strategies.stratvars]
|
||||
maxpozic = 200
|
||||
chunk = 10
|
||||
MA = 4
|
||||
Trend = 4
|
||||
profit = 0.01
|
||||
lastbuyindex=-6
|
||||
pendingbuys={}
|
||||
limitka = "None"
|
||||
|
||||
[[strategies.add_data]]
|
||||
symbol="C"
|
||||
rectype="bar"
|
||||
timeframe=10
|
||||
update_ltp=true
|
||||
align="round"
|
||||
mintick=0
|
||||
minsize=100
|
||||
exthours=false
|
||||
|
||||
#V3 na EPD
|
||||
[[strategies]]
|
||||
name = "V3 na EPD"
|
||||
symbol = "EPD"
|
||||
script = "ENTRY_backtest_strategyVykladaci"
|
||||
class = "StrategyOrderLimitVykladaci"
|
||||
open_rush = 0
|
||||
close_rush = 0
|
||||
|
||||
[strategies.stratvars]
|
||||
maxpozic = 200
|
||||
chunk = 10
|
||||
MA = 4
|
||||
Trend = 4
|
||||
profit = 0.02
|
||||
lastbuyindex=-6
|
||||
pendingbuys={}
|
||||
limitka = "None"
|
||||
jevylozeno=0
|
||||
vykladka=5
|
||||
curve = [0.01, 0.01, 0.01,0.01, 0.02, 0.01,0.01, 0.01,0.03, 0.01, 0.01, 0.01,0.04, 0.01,0.01, 0.01,0.05, 0.01,0.01, 0.01,0.01, 0.06,0.01, 0.01,0.01, 0.01]
|
||||
blockbuy = 0
|
||||
ticks2reset = 0.04
|
||||
|
||||
[[strategies.add_data]]
|
||||
symbol="EPD"
|
||||
rectype="bar"
|
||||
timeframe=15
|
||||
update_ltp=true
|
||||
align="round"
|
||||
mintick=0
|
||||
minsize=100
|
||||
exthours=false
|
||||
74
v2realbot/config.py
Normal file
74
v2realbot/config.py
Normal file
@ -0,0 +1,74 @@
|
||||
from alpaca.data.enums import DataFeed
|
||||
from v2realbot.enums.enums import Mode, Account
|
||||
from appdirs import user_data_dir
|
||||
|
||||
DATA_DIR = user_data_dir("v2realbot")
|
||||
#BT DELAYS
|
||||
|
||||
""""
|
||||
LATENCY DELAYS for LIVE eastcoast
|
||||
.000 trigger - last_trade_time (.4246266)
|
||||
+.020 vstup do strategie a BUY (.444606)
|
||||
+.023 submitted (.469198)
|
||||
+.008 filled (.476695552)
|
||||
+.023 fill not(.499888)
|
||||
"""
|
||||
#TODO změnit názvy delay promennych vystizneji a obecneji
|
||||
class BT_DELAYS:
|
||||
trigger_to_strat: float = 0.020
|
||||
strat_to_sub: float = 0.023
|
||||
sub_to_fill: float = 0.008
|
||||
fill_to_not: float = 0.023
|
||||
#doplnit dle live
|
||||
limit_order_offset: float = 0
|
||||
|
||||
class Keys:
|
||||
def __init__(self, api_key, secret_key, paper, feed) -> None:
|
||||
self.API_KEY = api_key
|
||||
self.SECRET_KEY = secret_key
|
||||
self.PAPER = paper
|
||||
self.FEED = feed
|
||||
|
||||
# podle modu (PAPER, LIVE) vrati objekt
|
||||
# obsahujici klice pro pripojeni k alpace
|
||||
def get_key(mode: Mode, account: Account):
|
||||
if mode not in [Mode.PAPER, Mode.LIVE]:
|
||||
print("has to be LIVE or PAPER only")
|
||||
return None
|
||||
dict = globals()
|
||||
try:
|
||||
API_KEY = dict[str.upper(str(account.value)) + "_" + str.upper(str(mode.value)) + "_API_KEY" ]
|
||||
SECRET_KEY = dict[str.upper(str(account.value)) + "_" + str.upper(str(mode.value)) + "_SECRET_KEY" ]
|
||||
PAPER = dict[str.upper(str(account.value)) + "_" + str.upper(str(mode.value)) + "_PAPER" ]
|
||||
FEED = dict[str.upper(str(account.value)) + "_" + str.upper(str(mode.value)) + "_FEED" ]
|
||||
return Keys(API_KEY, SECRET_KEY, PAPER, FEED)
|
||||
except KeyError:
|
||||
print("Not valid combination to get keys for", mode, account)
|
||||
return 0
|
||||
|
||||
#strategy instance main loop heartbeat
|
||||
HEARTBEAT_TIMEOUT=5
|
||||
|
||||
WEB_API_KEY="david"
|
||||
|
||||
#PRIMARY PAPER
|
||||
ACCOUNT1_PAPER_API_KEY = 'PKGGEWIEYZOVQFDRY70L'
|
||||
ACCOUNT1_PAPER_SECRET_KEY = 'O5Kt8X4RLceIOvM98i5LdbalItsX7hVZlbPYHy8Y'
|
||||
ACCOUNT1_PAPER_MAX_BATCH_SIZE = 1
|
||||
ACCOUNT1_PAPER_PAPER = True
|
||||
ACCOUNT1_PAPER_FEED = DataFeed.SIP
|
||||
|
||||
#PRIMARY LIVE
|
||||
ACCOUNT1_LIVE_API_KEY = 'AKB5HD32LPDZC9TPUWJT'
|
||||
ACCOUNT1_LIVE_SECRET_KEY = 'Xq1wPSNOtwmlMTAd4cEmdKvNDgfcUYfrOaCccaAs'
|
||||
ACCOUNT1_LIVE_MAX_BATCH_SIZE = 1
|
||||
ACCOUNT1_LIVE_PAPER = False
|
||||
ACCOUNT1_LIVE_FEED = DataFeed.SIP
|
||||
|
||||
#SECONDARY PAPER
|
||||
ACCOUNT2_PAPER_API_KEY = 'PKQEAAJTVC72SZO3CT3R'
|
||||
ACCOUNT2_PAPER_SECRET_KEY = 'mqdftzGJlJdvUjdsuQynAURCHRwAI0a8nhJy8nyz'
|
||||
ACCOUNT2_PAPER_MAX_BATCH_SIZE = 1
|
||||
ACCOUNT2_PAPER_PAPER = True
|
||||
ACCOUNT2_PAPER_FEED = DataFeed.IEX
|
||||
|
||||
0
v2realbot/controller/__init__.py
Normal file
0
v2realbot/controller/__init__.py
Normal file
BIN
v2realbot/controller/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
v2realbot/controller/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
v2realbot/controller/__pycache__/services.cpython-311.pyc
Normal file
BIN
v2realbot/controller/__pycache__/services.cpython-311.pyc
Normal file
Binary file not shown.
317
v2realbot/controller/services.py
Normal file
317
v2realbot/controller/services.py
Normal file
@ -0,0 +1,317 @@
|
||||
from typing import Any, List
|
||||
from uuid import UUID, uuid4
|
||||
import pickle
|
||||
from v2realbot.enums.enums import RecordType, StartBarAlign, Mode, Account
|
||||
from v2realbot.common.model import StrategyInstance, Runner, RunRequest
|
||||
from v2realbot.utils.utils import AttributeDict, zoneNY, dict_replace_value, Store, parse_toml_string
|
||||
from datetime import datetime
|
||||
from threading import Thread, current_thread, Event, enumerate
|
||||
import importlib
|
||||
from queue import Queue
|
||||
db = Store()
|
||||
|
||||
def get_all_threads():
|
||||
res = str(enumerate())
|
||||
if len(res) > 0:
|
||||
return (0, res)
|
||||
else:
|
||||
return (-2, "not found")
|
||||
|
||||
def get_all_runners():
|
||||
if len(db.runners) > 0:
|
||||
print(db.runners)
|
||||
return (0, db.runners)
|
||||
else:
|
||||
return (0, [])
|
||||
|
||||
def get_all_stratins():
|
||||
if len(db.stratins) > 0:
|
||||
return (0, db.stratins)
|
||||
else:
|
||||
return (0, [])
|
||||
|
||||
def get_stratin(id: UUID):
|
||||
for i in db.stratins:
|
||||
if str(i.id) == str(id):
|
||||
return (0, i)
|
||||
return (-2, "not found")
|
||||
|
||||
def get_runner(id: UUID):
|
||||
for i in db.runners:
|
||||
if str(i.id) == str(id):
|
||||
return (0, i)
|
||||
return (-2, "not found")
|
||||
|
||||
def create_stratin(si: StrategyInstance):
|
||||
#validate toml
|
||||
res, stp = parse_toml_string(si.stratvars_conf)
|
||||
if res < 0:
|
||||
return (-1,"stratvars invalid")
|
||||
res, adp = parse_toml_string(si.add_data_conf)
|
||||
if res < 0:
|
||||
return (-1, "None")
|
||||
si.id = uuid4()
|
||||
print(si)
|
||||
db.stratins.append(si)
|
||||
db.save()
|
||||
#print(db.stratins)
|
||||
return (0,si.id)
|
||||
|
||||
def modify_stratin(si: StrategyInstance, id: UUID):
|
||||
#validate toml if fields exists
|
||||
if is_stratin_running(id):
|
||||
return (-1, "strat is running, use modify_stratin_running")
|
||||
res, stp = parse_toml_string(si.stratvars_conf)
|
||||
if res < 0:
|
||||
return (-1, "stratvars invalid")
|
||||
res, adp = parse_toml_string(si.add_data_conf)
|
||||
if res < 0:
|
||||
return (-1, "add data conf invalid")
|
||||
for i in db.stratins:
|
||||
if str(i.id) == str(id):
|
||||
print("removing",i)
|
||||
db.stratins.remove(i)
|
||||
print("adding",si)
|
||||
db.stratins.append(si)
|
||||
print(db.stratins)
|
||||
db.save()
|
||||
return (0, i.id)
|
||||
return (-2, "not found")
|
||||
|
||||
def delete_stratin(id: UUID):
|
||||
if is_stratin_running(id=str(id)):
|
||||
return (-1, "Strategy Instance is running " + str(id))
|
||||
for i in db.stratins:
|
||||
if str(i.id) == str(id):
|
||||
db.stratins.remove(i)
|
||||
db.save()
|
||||
print(db.stratins)
|
||||
return (0, i.id)
|
||||
return (-2, "not found")
|
||||
|
||||
def inject_stratvars(id: UUID, stratvars_parsed: AttributeDict):
|
||||
for i in db.runners:
|
||||
if str(i.id) == str(id):
|
||||
i.run_instance.state.vars = AttributeDict(stratvars_parsed["stratvars"])
|
||||
i.run_instance.stratvars = AttributeDict(stratvars_parsed["stratvars"])
|
||||
return 0
|
||||
return -2
|
||||
|
||||
#allows change of set of parameters that are possible to change while it is running
|
||||
#also injects those parameters to instance
|
||||
def modify_stratin_running(si: StrategyInstance, id: UUID):
|
||||
#validate toml
|
||||
res,stp = parse_toml_string(si.stratvars_conf)
|
||||
if res < 0:
|
||||
return (-1, "stratvars invalid")
|
||||
for i in db.stratins:
|
||||
if str(i.id) == str(id):
|
||||
if not is_stratin_running(id=str(id)):
|
||||
return (-1, "not running")
|
||||
i.id2 = si.id2
|
||||
i.name = si.name
|
||||
i.open_rush = si.open_rush
|
||||
i.stratvars_conf = si.stratvars_conf
|
||||
i.note = si.note
|
||||
i.history = si.history
|
||||
db.save()
|
||||
#TODO reload running strat
|
||||
print(stp)
|
||||
print("starting injection", stp)
|
||||
res = inject_stratvars(id=si.id, stratvars_parsed=stp)
|
||||
if res < 0:
|
||||
print("ajajaj inject se nepovedl")
|
||||
return(-3, "inject failed")
|
||||
return (0, i.id)
|
||||
return (-2, "not found")
|
||||
#controller.reload_params(si)
|
||||
|
||||
##enable realtime chart - inject given queue for strategy instance
|
||||
##webservice listens to this queue
|
||||
async def stratin_realtime_on(id: UUID, rtqueue: Queue):
|
||||
for i in db.runners:
|
||||
if str(i.id) == str(id):
|
||||
i.run_instance.rtqueue = rtqueue
|
||||
print("RT QUEUE added")
|
||||
return 0
|
||||
print("ERROR NOT FOUND")
|
||||
return -2
|
||||
|
||||
async def stratin_realtime_off(id: UUID):
|
||||
for i in db.runners:
|
||||
if str(i.id) == str(id):
|
||||
i.run_instance.rtqueue = None
|
||||
print("RT QUEUE removed")
|
||||
return 0
|
||||
print("ERROR NOT FOUND")
|
||||
return -2
|
||||
|
||||
##controller (run_stratefy, pause, stop, reload_params)
|
||||
def pause_stratin(id: UUID):
|
||||
for i in db.runners:
|
||||
print(i.id)
|
||||
if str(i.id) == id:
|
||||
if i.run_pause_ev.is_set():
|
||||
i.run_pause_ev.clear()
|
||||
i.run_paused = None
|
||||
print("Unpaused")
|
||||
return (0, "unpaused runner " + str(i.id))
|
||||
print("pausing runner", i.id)
|
||||
i.run_pause_ev.set()
|
||||
i.run_paused = datetime.now().astimezone(zoneNY)
|
||||
return (0, "paused runner " + str(i.id))
|
||||
print("no ID found")
|
||||
return (-1, "not running instance found")
|
||||
|
||||
def stop_stratin(id: UUID = None):
|
||||
chng = []
|
||||
for i in db.runners:
|
||||
#print(i['id'])
|
||||
if id is None or str(i.id) == id:
|
||||
chng.append(i.id)
|
||||
print("Sending STOP signal to Runner", i.id)
|
||||
#just sending the signal, update is done in stop after plugin
|
||||
i.run_stop_ev.set()
|
||||
# i.run_stopped = datetime.now().astimezone(zoneNY)
|
||||
# i.run_thread = None
|
||||
# i.run_instance = None
|
||||
# i.run_pause_ev = None
|
||||
# i.run_stop_ev = None
|
||||
# #stratins.remove(i)
|
||||
if len(chng) > 0:
|
||||
return (0, "Sent STOP signal to those" + str(chng))
|
||||
else:
|
||||
return (-2, "not found" + str(id))
|
||||
|
||||
def is_stratin_running(id: UUID):
|
||||
for i in db.runners:
|
||||
if str(i.id) == str(id):
|
||||
if i.run_started is not None and i.run_stopped is None:
|
||||
return True
|
||||
return False
|
||||
|
||||
def save_history(id: UUID, st: object, runner: Runner, reason: str = None):
|
||||
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 += str(runner.__dict__)+"<BR>"
|
||||
db.save()
|
||||
|
||||
#Capsule to run the thread in. Needed in order to update db after strat ends for any reason#
|
||||
def capsule(target: object, db: object):
|
||||
|
||||
#TODO zde odchytit pripadnou exceptionu a zapsat do history
|
||||
#cil aby padnuti jedne nezpusobilo pad enginu
|
||||
try:
|
||||
target.start()
|
||||
print("Strategy instance stopped. Update runners")
|
||||
reason = "SHUTDOWN OK"
|
||||
except Exception as e:
|
||||
reason = "SHUTDOWN Exception:" + str(e)
|
||||
finally:
|
||||
# remove runners after thread is stopped and save results to stratin history
|
||||
for i in db.runners:
|
||||
if i.run_instance == target:
|
||||
i.run_stopped = datetime.now().astimezone(zoneNY)
|
||||
i.run_thread = None
|
||||
i.run_instance = None
|
||||
i.run_pause_ev = None
|
||||
i.run_stop_ev = None
|
||||
#ukladame radek do historie (pozdeji refactor)
|
||||
save_history(id=i.id, st=target, runner=i, reason=reason)
|
||||
#mazeme runner po skonceni instance
|
||||
db.runners.remove(i)
|
||||
|
||||
print("Runner STOPPED")
|
||||
|
||||
def run_stratin(id: UUID, runReq: RunRequest):
|
||||
if runReq.mode == Mode.BT:
|
||||
if runReq.bt_from is None:
|
||||
return (-1, "start date required for BT")
|
||||
if runReq.bt_to is None:
|
||||
runReq.bt_to = datetime.now().astimezone(zoneNY)
|
||||
|
||||
print("hodnota ID pred",id)
|
||||
#volani funkce instantiate_strategy
|
||||
for i in db.stratins:
|
||||
if str(i.id) == str(id):
|
||||
try:
|
||||
if is_stratin_running(id=id):
|
||||
return(-1, "already running")
|
||||
#validate toml
|
||||
res, stp = parse_toml_string(i.stratvars_conf)
|
||||
if res < 0:
|
||||
return (-1, "stratvars invalid")
|
||||
res, adp = parse_toml_string(i.add_data_conf)
|
||||
if res < 0:
|
||||
return (-1, "add data conf invalid")
|
||||
print("jsme uvnitr")
|
||||
id = uuid4()
|
||||
name = i.name
|
||||
symbol = i.symbol
|
||||
open_rush = i.open_rush
|
||||
close_rush = i.close_rush
|
||||
#do budoucna vylepsit konfiguraci, udelat na frontendu jedno pole config
|
||||
#obsahujici cely toml dane strategie
|
||||
#nyni predpokladame, ze stratvars a add_data sloupce v gui obsahuji
|
||||
#dany TOML element
|
||||
try:
|
||||
stratvars = AttributeDict(stp["stratvars"])
|
||||
except KeyError:
|
||||
return (-1, "stratvars musi obsahovat element [stratvars]")
|
||||
classname = i.class_name
|
||||
script = "v2realbot."+i.script
|
||||
pe = Event()
|
||||
se = Event()
|
||||
|
||||
import_script = importlib.import_module(script)
|
||||
next = getattr(import_script, "next")
|
||||
init = getattr(import_script, "init")
|
||||
my_module = importlib.import_module("v2realbot.strategy."+classname)
|
||||
StrategyClass = getattr(my_module, classname)
|
||||
#instance strategie
|
||||
instance = StrategyClass(name= name,
|
||||
symbol=symbol,
|
||||
account=runReq.account,
|
||||
next=next,
|
||||
init=init,
|
||||
stratvars=stratvars,
|
||||
open_rush=open_rush, close_rush=close_rush, pe=pe, se=se)
|
||||
print("instance vytvorena", instance)
|
||||
#set mode
|
||||
if runReq.mode == Mode.LIVE or runReq.mode == Mode.PAPER:
|
||||
instance.set_mode(mode=runReq.mode, debug = runReq.debug)
|
||||
else:
|
||||
instance.set_mode(mode = Mode.BT,
|
||||
debug = runReq.debug,
|
||||
start = runReq.bt_from.astimezone(zoneNY),
|
||||
end = runReq.bt_to.astimezone(zoneNY),
|
||||
cash=runReq.cash)
|
||||
##add data streams
|
||||
for st in adp["add_data"]:
|
||||
print("adding stream", st)
|
||||
instance.add_data(**st)
|
||||
|
||||
print("Starting strategy", instance.name)
|
||||
#vlakno = Thread(target=instance.start, name=instance.name)
|
||||
#pokus na spusteni v kapsli, abychom po skonceni mohli updatnout stratin
|
||||
vlakno = Thread(target=capsule, args=(instance,db), name=instance.name)
|
||||
vlakno.start()
|
||||
print("Spuštěna", instance.name)
|
||||
##storing the attributtes - pozor pri stopu je zase odstranit
|
||||
runner = Runner(id = i.id,
|
||||
run_started = datetime.now(zoneNY),
|
||||
run_pause_ev = pe,
|
||||
run_stop_ev = se,
|
||||
run_thread = vlakno,
|
||||
run_account = runReq.account,
|
||||
run_mode = runReq.mode,
|
||||
run_instance = instance)
|
||||
db.runners.append(runner)
|
||||
print(db.runners)
|
||||
print(i)
|
||||
print(enumerate())
|
||||
return (0, i.id)
|
||||
except Exception as e:
|
||||
return (-2, "Exception: "+str(e))
|
||||
return (-2, "not found")
|
||||
27
v2realbot/emptystrategy.py
Normal file
27
v2realbot/emptystrategy.py
Normal file
@ -0,0 +1,27 @@
|
||||
from strategy import MyStrategy, StrategyState
|
||||
from enums import RecordType, StartBarAlign
|
||||
from config import API_KEY, SECRET_KEY, MAX_BATCH_SIZE, PAPER
|
||||
from indicators import ema
|
||||
from rich import print
|
||||
|
||||
def next(data, state: StrategyState):
|
||||
print("next")
|
||||
print(state.variables.MA)
|
||||
print(state.variables.maxpozic)
|
||||
print(data)
|
||||
print(state.oe.pos())
|
||||
|
||||
def init(state: StrategyState):
|
||||
print("init - zatim bez data")
|
||||
print(state.oe.symbol)
|
||||
print(state.oe.pos())
|
||||
print()
|
||||
|
||||
def main():
|
||||
stratvars = dict(maxpozic = 10, chunk = 1, MA = 3, Trend = 4,profit = 0.01)
|
||||
s = MyStrategy("TSLA",paper=PAPER, next=next, init=init, stratvars=stratvars)
|
||||
s.add_data(symbol="TSLA",rectype=RecordType.TRADE,timeframe=5,filters=None,update_ltp=True,align=StartBarAlign.ROUND,mintick=0)
|
||||
s.start()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
0
v2realbot/enums/__init__.py
Normal file
0
v2realbot/enums/__init__.py
Normal file
BIN
v2realbot/enums/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
v2realbot/enums/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
v2realbot/enums/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
v2realbot/enums/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
v2realbot/enums/__pycache__/enums.cpython-310.pyc
Normal file
BIN
v2realbot/enums/__pycache__/enums.cpython-310.pyc
Normal file
Binary file not shown.
BIN
v2realbot/enums/__pycache__/enums.cpython-311.pyc
Normal file
BIN
v2realbot/enums/__pycache__/enums.cpython-311.pyc
Normal file
Binary file not shown.
BIN
v2realbot/enums/__pycache__/myenums.cpython-311.pyc
Normal file
BIN
v2realbot/enums/__pycache__/myenums.cpython-311.pyc
Normal file
Binary file not shown.
47
v2realbot/enums/enums.py
Normal file
47
v2realbot/enums/enums.py
Normal file
@ -0,0 +1,47 @@
|
||||
from enum import Enum
|
||||
from alpaca.trading.enums import OrderSide, OrderStatus
|
||||
class Order:
|
||||
def __init__(self, id: str, status: OrderStatus, side: OrderSide, symbol: str, qty: int, limit_price: float = None, filled_qty: int = 0, filled_avg_price: float = 0, filled_time: float = None) -> None:
|
||||
self.id = id
|
||||
self.status = status
|
||||
self.side = side
|
||||
self.symbol = symbol
|
||||
self.qty = qty
|
||||
self.filled_qty = filled_qty
|
||||
self.filled_avg_price = filled_avg_price
|
||||
self.filled_time = filled_time
|
||||
self.limit_price = limit_price
|
||||
|
||||
class Account(Enum):
|
||||
"""
|
||||
Accounts - keys to config
|
||||
"""
|
||||
ACCOUNT1 = "ACCOUNT1"
|
||||
ACCOUNT2 = "ACCOUNT2"
|
||||
class RecordType(Enum):
|
||||
"""
|
||||
Represents output of aggregator
|
||||
"""
|
||||
|
||||
BAR = "bar"
|
||||
CBAR = "continuosbar"
|
||||
TRADE = "trade"
|
||||
|
||||
class Mode(Enum):
|
||||
"""
|
||||
LIVE or BT
|
||||
"""
|
||||
|
||||
PAPER = "paper"
|
||||
LIVE = "live"
|
||||
BT = "backtest"
|
||||
|
||||
|
||||
class StartBarAlign(Enum):
|
||||
"""
|
||||
Represents first bar start time alignement according to timeframe
|
||||
ROUND = bar starts at 0,5,10 (for 5s timeframe)
|
||||
RANDOM = first bar starts when first trade occurs
|
||||
"""
|
||||
ROUND = "round"
|
||||
RANDOM = "random"
|
||||
0
v2realbot/indicators/__init__.py
Normal file
0
v2realbot/indicators/__init__.py
Normal file
BIN
v2realbot/indicators/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
v2realbot/indicators/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
v2realbot/indicators/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
v2realbot/indicators/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
v2realbot/indicators/__pycache__/indicators.cpython-310.pyc
Normal file
BIN
v2realbot/indicators/__pycache__/indicators.cpython-310.pyc
Normal file
Binary file not shown.
BIN
v2realbot/indicators/__pycache__/indicators.cpython-311.pyc
Normal file
BIN
v2realbot/indicators/__pycache__/indicators.cpython-311.pyc
Normal file
Binary file not shown.
36
v2realbot/indicators/indicators.py
Normal file
36
v2realbot/indicators/indicators.py
Normal file
@ -0,0 +1,36 @@
|
||||
import tulipy as ti
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from collections import deque
|
||||
import typing
|
||||
|
||||
def ema(data, period: int = 50, use_series=False):
|
||||
if check_series(data):
|
||||
use_series = True
|
||||
data = convert_to_numpy(data)
|
||||
ema = ti.ema(data, period=period)
|
||||
return pd.Series(ema) if use_series else ema
|
||||
|
||||
def sma(data, period: int = 50, use_series=False):
|
||||
"""
|
||||
Finding the moving average of a dataset
|
||||
Args:
|
||||
data: (list) A list containing the data you want to find the moving average of
|
||||
period: (int) How far each average set should be
|
||||
"""
|
||||
if check_series(data):
|
||||
use_series = True
|
||||
data = convert_to_numpy(data)
|
||||
sma = ti.sma(data, period=period)
|
||||
return pd.Series(sma) if use_series else sma
|
||||
|
||||
def convert_to_numpy(data):
|
||||
if isinstance(data, list) or isinstance(data, deque):
|
||||
return np.fromiter(data, float)
|
||||
elif isinstance(data, pd.Series):
|
||||
return data.to_numpy()
|
||||
return data
|
||||
|
||||
|
||||
def check_series(data):
|
||||
return isinstance(data, pd.Series)
|
||||
119
v2realbot/indicators/moving_averages.py
Normal file
119
v2realbot/indicators/moving_averages.py
Normal file
@ -0,0 +1,119 @@
|
||||
"""
|
||||
Moving average function utils
|
||||
Copyright (C) 2021 Brandon Fan
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
|
||||
|
||||
#inspirovat se, pripadne vyzkouset i TAlib
|
||||
|
||||
from typing import Any
|
||||
|
||||
import pandas as pd
|
||||
import tulipy as ti
|
||||
|
||||
def convert_to_numpy(data: Any):
|
||||
if isinstance(data, list) or isinstance(data, deque):
|
||||
return np.fromiter(data, float)
|
||||
elif isinstance(data, pd.Series):
|
||||
return data.to_numpy()
|
||||
return data
|
||||
|
||||
|
||||
def check_series(data: Any):
|
||||
return isinstance(data, pd.Series)
|
||||
|
||||
def ema(data: Any, period: int = 50, use_series=False) -> Any:
|
||||
if check_series(data):
|
||||
use_series = True
|
||||
data = convert_to_numpy(data)
|
||||
ema = ti.ema(data, period=period)
|
||||
return pd.Series(ema) if use_series else ema
|
||||
|
||||
|
||||
def vwma(data: Any, volume_data: Any, period: int = 50, use_series=False) -> Any:
|
||||
if check_series(data):
|
||||
use_series = True
|
||||
|
||||
data = convert_to_numpy(data)
|
||||
volume_data = convert_to_numpy(volume_data).astype(float)
|
||||
|
||||
vwma = ti.vwma(data, volume_data, period=period)
|
||||
return pd.Series(vwma) if use_series else vwma
|
||||
|
||||
|
||||
def wma(data: Any, period: int = 50, use_series=False) -> Any:
|
||||
if check_series(data):
|
||||
use_series = True
|
||||
data = convert_to_numpy(data)
|
||||
wma = ti.wma(data, period)
|
||||
return pd.Series(wma) if use_series else wma
|
||||
|
||||
|
||||
def zlema(data: Any, period: int = 50, use_series=False) -> Any:
|
||||
if check_series(data):
|
||||
use_series = True
|
||||
data = convert_to_numpy(data)
|
||||
zlema = ti.zlema(data, period)
|
||||
return pd.Series(zlema) if use_series else zlema
|
||||
|
||||
|
||||
def sma(data: Any, period: int = 50, use_series=False) -> Any:
|
||||
"""
|
||||
Finding the moving average of a dataset
|
||||
Args:
|
||||
data: (list) A list containing the data you want to find the moving average of
|
||||
period: (int) How far each average set should be
|
||||
"""
|
||||
if check_series(data):
|
||||
use_series = True
|
||||
data = convert_to_numpy(data)
|
||||
sma = ti.sma(data, period=period)
|
||||
return pd.Series(sma) if use_series else sma
|
||||
|
||||
|
||||
def hma(data: Any, period: int = 50, use_series=False) -> Any:
|
||||
if check_series(data):
|
||||
use_series = True
|
||||
data = convert_to_numpy(data)
|
||||
hma = ti.hma(data, period)
|
||||
return pd.Series(hma) if use_series else hma
|
||||
|
||||
|
||||
def kaufman_adaptive_ma(data: Any, period: int = 50, use_series=False) -> Any:
|
||||
if check_series(data):
|
||||
use_series = True
|
||||
data = convert_to_numpy(data)
|
||||
kama = ti.kama(data, period)
|
||||
return pd.Series(kama) if use_series else kama
|
||||
|
||||
|
||||
def trima(data: Any, period: int = 50, use_series=False) -> Any:
|
||||
if check_series(data):
|
||||
use_series = True
|
||||
data = convert_to_numpy(data)
|
||||
trima = ti.trima(data, period)
|
||||
return pd.Series(trima) if use_series else trima
|
||||
|
||||
|
||||
def macd(data: Any, short_period: int = 12, long_period: int = 26, signal_period: int = 9, use_series=False) -> Any:
|
||||
if check_series(data):
|
||||
use_series = True
|
||||
data = convert_to_numpy(data)
|
||||
macd, macd_signal, macd_histogram = ti.macd(data, short_period, long_period, signal_period)
|
||||
if use_series:
|
||||
df = pd.DataFrame({'macd': macd, 'macd_signal': macd_signal, 'macd_histogram': macd_histogram})
|
||||
return df
|
||||
return macd, macd_signal, macd_histogram
|
||||
0
v2realbot/interfaces/__init__.py
Normal file
0
v2realbot/interfaces/__init__.py
Normal file
BIN
v2realbot/interfaces/__pycache__/__init__.cpython-310.pyc
Normal file
BIN
v2realbot/interfaces/__pycache__/__init__.cpython-310.pyc
Normal file
Binary file not shown.
BIN
v2realbot/interfaces/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
v2realbot/interfaces/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
v2realbot/interfaces/__pycache__/live_interface.cpython-310.pyc
Normal file
BIN
v2realbot/interfaces/__pycache__/live_interface.cpython-310.pyc
Normal file
Binary file not shown.
BIN
v2realbot/interfaces/__pycache__/live_interface.cpython-311.pyc
Normal file
BIN
v2realbot/interfaces/__pycache__/live_interface.cpython-311.pyc
Normal file
Binary file not shown.
BIN
v2realbot/interfaces/__pycache__/orders.cpython-311.pyc
Normal file
BIN
v2realbot/interfaces/__pycache__/orders.cpython-311.pyc
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user