first commit
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
/.DS_Store
|
||||||
|
/v2realbot.egg-info
|
||||||
@@ -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
|
||||||
Vendored
+18
@@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Vendored
+6
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"python.analysis.logLevel": "Trace",
|
||||||
|
"terminal.integrated.env.osx": {
|
||||||
|
"PYTHONPATH": "${workspaceFolder}/"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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()
|
||||||
|
)
|
||||||
@@ -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/
|
||||||
Binary file not shown.
@@ -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)
|
||||||
|
|
||||||
|
|
||||||
@@ -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()
|
||||||
@@ -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
|
||||||
@@ -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"]))
|
||||||
@@ -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)
|
||||||
@@ -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?")
|
||||||
@@ -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))
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
@@ -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)
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
API_KEY = 'PKGGEWIEYZOVQFDRY70L'
|
||||||
|
SECRET_KEY = 'O5Kt8X4RLceIOvM98i5LdbalItsX7hVZlbPYHy8Y'
|
||||||
|
MAX_BATCH_SIZE = 1
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import panel as pn
|
||||||
|
app = pn.Column("DDD")
|
||||||
|
# app.servable()
|
||||||
|
# app.append("Nice")
|
||||||
|
app.show()
|
||||||
@@ -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')
|
||||||
|
|
||||||
@@ -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()
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -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()
|
||||||
@@ -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()
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -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()
|
||||||
@@ -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)
|
||||||
|
|
||||||
|
|
||||||
@@ -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()
|
||||||
|
|
||||||
|
##
|
||||||
@@ -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")
|
||||||
@@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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")
|
||||||
@@ -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)
|
||||||
@@ -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))
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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()
|
||||||
|
|
||||||
@@ -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)
|
||||||
@@ -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)
|
||||||
@@ -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()
|
||||||
|
|
||||||
@@ -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()
|
||||||
|
|
||||||
@@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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()
|
||||||
|
|
||||||
@@ -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()
|
||||||
@@ -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.
|
||||||
|
|
||||||
@@ -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()
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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?")
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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
|
||||||
|
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|
||||||
Binary file not shown.
Binary file not shown.
@@ -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")
|
||||||
@@ -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()
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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"
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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)
|
||||||
@@ -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
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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