Skip to content

Commit

Permalink
1. depracate old backtester code,
Browse files Browse the repository at this point in the history
2. adopt streamlit support
  • Loading branch information
amor71 committed Jun 6, 2022
1 parent 8850afc commit d00ab72
Show file tree
Hide file tree
Showing 5 changed files with 47 additions and 934 deletions.
165 changes: 15 additions & 150 deletions analysis/backtester_ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,10 @@
asyncio.set_event_loop(loop)
import json
import traceback
from datetime import date, timedelta

import alpaca_trade_api as tradeapi
import matplotlib.pyplot as plt
import nest_asyncio
import pandas as pd
import pygit2
import pytz
import requests
import streamlit as st
Expand All @@ -20,150 +17,20 @@
from liualgotrader.analytics.analysis import (calc_batch_revenue, count_trades,
load_batch_list, load_trades,
load_trades_by_batch_id)
from liualgotrader.backtester import BackTestDay, backtest
from liualgotrader.common import config, database
from liualgotrader.common.data_loader import DataLoader # type: ignore

try:
config.build_label = pygit2.Repository("../").describe(
describe_strategy=pygit2.GIT_DESCRIBE_TAGS
)
except pygit2.GitError:
import liualgotrader

config.build_label = liualgotrader.__version__ if hasattr(liualgotrader, "__version__") else "" # type: ignore


nest_asyncio.apply()


loop.run_until_complete(database.create_db_connection())
st.title("Liu Algo Trading Framework")
st.markdown("## **Back-testing & Analysis tools**")
st.title(f"LiuAlgoTrading Framework")
st.markdown("## **Visual Analysis tools**")

app = st.sidebar.selectbox("select app", ("back-test", "analyzer"))
app = st.sidebar.selectbox("select app", ["analyzer"])

new_bid: str = ""
est = pytz.timezone("America/New_York")

if app == "back-test":
new_bid = ""
st.text("Back-testing a past trading day, or a specific batch-id.")

day_to_analyze = st.date_input("Pick day to analyze", value=date.today())

selection = st.sidebar.radio(
"Select back-testing mode",
(
"back-test a specific batch",
# "back-test against the whole day",
),
)

async def back_test():
uploaded_file: UploadedFile = st.file_uploader(
label="Select tradeplan file", type=["toml", "TOML"]
)
if uploaded_file:
byte_str = uploaded_file.read()
toml_as_string = byte_str.decode("UTF-8")

if len(toml_as_string):
conf_dict = toml.loads(toml_as_string) # type: ignore

if not conf_dict:
st.error("Failed to load TOML configuration file, retry")
st.stop()
else:
st.stop()
else:
st.stop()

backtest = BackTestDay(conf_dict)
new_bid = await backtest.create(day_to_analyze)
while True:
status, msgs = await backtest.next_minute()
if not status:
break
if len(msgs) > 0:
for msg in msgs:
if msg:
st.success(msg)

await backtest.liquidate()
st.success(f"new batch-id is {new_bid} -> view in the analyzer app")

if selection == "back-test against the whole day":
with st.spinner(f"back-testing.. patience is a virtue "):
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.run_until_complete(back_test())
else:
try:
with st.spinner("Loading list of trading sessions"):
df = load_batch_list(day_to_analyze, "")
if df.empty:
st.error("Select another day")
st.stop()
except Exception as e:
st.error("Select another day")
st.exception(e)
st.stop()
bid = st.selectbox(
"select batch to backtest", df["batch_id"].unique().tolist()
)
if bid:
how_was_my_day = pd.DataFrame()
trades = load_trades(day_to_analyze)
start_time = df[df.batch_id == bid].start_time
start_time = pytz.utc.localize(start_time.min()).astimezone(est)
how_was_my_day["symbol"] = trades.loc[trades["batch_id"] == bid][
"symbol"
].unique()
how_was_my_day["revenues"] = how_was_my_day["symbol"].apply(
lambda x: calc_batch_revenue(x, trades, bid)
)
how_was_my_day["count"] = how_was_my_day["symbol"].apply(
lambda x: count_trades(x, trades, bid)
)
st.text(
f"batch_id:{bid}\nstart time:{start_time }\nrevenue=${round(sum(how_was_my_day['revenues']), 2)}"
)
st.dataframe(how_was_my_day)

uploaded_file: UploadedFile = st.file_uploader(
label="Select tradeplan file", type=["toml", "TOML"]
)
if uploaded_file:
byte_str = uploaded_file.read()
toml_as_string = byte_str.decode("UTF-8")

if len(toml_as_string):
conf_dict = toml.loads(toml_as_string) # type: ignore

if not conf_dict:
st.error(
"Failed to load TOML configuration file, retry"
)
st.stop()
else:
st.stop()
with st.spinner(f"back-testing.."):
asyncio.set_event_loop(asyncio.new_event_loop())
try:
print(bid)
new_bid = backtest(bid, conf_dict=conf_dict, debug_symbols=[]) # type: ignore
except Exception as e:
st.exception(e)
st.stop()

st.success(
f"new batch-id is {new_bid} -> view in the analyzer app"
)


elif app == "analyzer":
st.text("Analyze a specific batch-id")
if app == "analyzer":
st.text("Analyze a batch-id")

show_trade_details = st.sidebar.checkbox("show trade details")
bid = st.text_input("Enter batch-id", value=new_bid)
Expand All @@ -173,14 +40,19 @@ async def back_test():
utc = pytz.timezone("UTC")
try:
how_was_my_batch = pd.DataFrame()
t = load_trades_by_batch_id(bid)
t = loop.run_until_complete(load_trades_by_batch_id(bid))

if not len(t["client_time"].tolist()):
print(t)

if t.empty:
st.info("No trades, select another batch")
st.stop()
elif not len(t["client_time"].tolist()):
st.info("No trades, $0 revenue")
st.stop()

if show_trade_details:
elif show_trade_details:
st.dataframe(t)

how_was_my_batch["symbol"] = t.symbol.unique()
how_was_my_batch["revenues"] = how_was_my_batch["symbol"].apply(
lambda x: calc_batch_revenue(x, t, bid)
Expand All @@ -205,17 +77,10 @@ async def back_test():

data_loader = DataLoader()
day_to_analyze = min(t["client_time"].tolist())
with st.spinner(text="Loading historical data from Polygon..."):
with st.spinner(text="Loading historical data"):
for symbol in t.symbol.unique().tolist():
if symbol not in minute_history:
minute_history[symbol] = data_loader[symbol]
# api.polygon.historic_agg_v2(
# symbol,
# 1,
# "minute",
# _from=day_to_analyze - timedelta(days=7),
# to=day_to_analyze + timedelta(days=1),
# ).df.tz_convert("US/Eastern")
c += 1
st.success(f"LOADED {c} symbols' data!")
est = pytz.timezone("US/Eastern")
Expand Down
2 changes: 1 addition & 1 deletion liualgotrader/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "0.4.22"
__version__ = "0.4.23"
81 changes: 30 additions & 51 deletions liualgotrader/backtester
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,35 @@ from liualgotrader.models.trending_tickers import TrendingTickers
from liualgotrader.strategies.base import Strategy, StrategyType
from liualgotrader.scanners.base import Scanner
from liualgotrader.scanners.momentum import Momentum
from liualgotrader import backtester
from liualgotrader import enhanced_backtest


def show_usage():
print(
f"\n{sys.argv[0]} from <start_date> [--asset=equity(DEFAULT)|crypto][--scanners=<scanner-name,>] [--strats=<strategy-name,>] [--to=<end_date> DEFAULT is today] [--scale=day(DEFAULT)|minute [--buy-fee-percentage=0.(DEFAULT)] [--sell-fee-percentage=0.(DEFAULT)]",
)

print("\n\noptions:")
print(
"asset\t\t\tAsset type being traded. equity = US Equities, crypt = Crypto-currency. Asset type affects the market schedule for backtesting."
)
print(
"to\t\t\tdate string in the format YYYY-MM-DD, if not provided current day is selected"
)
print("scale\t\t\ttime-scale for loading past data for back-test-ing")
print(
"buy-fee-percentage\tBroker fees as percentage from transaction. Represented as 0-1."
)
print(
"sell-fee-percentage\tBroker fees as percentage from transaction. Represented as 0-1."
)


def show_version(filename: str, version: str) -> None:
"""Display welcome message"""
print(f"filename:{filename}\ngit version:{version}\n")


def dateFromString(s: str) -> date:
c = pdt.Calendar()
result, what = c.parse(s)
Expand Down Expand Up @@ -68,7 +93,7 @@ if __name__ == "__main__":
config.build_label = liualgotrader.__version__ if hasattr(liualgotrader, "__version__") else "" # type: ignore

if len(sys.argv) == 1:
backtester.show_usage()
show_usage()
sys.exit(0)

config.filename = os.path.basename(__file__)
Expand All @@ -90,53 +115,7 @@ if __name__ == "__main__":
if "risk" in conf_dict:
config.risk = conf_dict["risk"]

if sys.argv[1] == "batch":
try:
strict = False
opts, args = getopt.getopt(
sys.argv[2:],
"b:d:s",
["batch-list", "debug=", "strict", "symbol=", "duration="],
)
debug_symbols = []
symbols = None
duration: int = None
for opt, arg in opts:
if opt in ("--batch-list", "-b"):
backtester.get_batch_list()
break
elif opt in ("--debug", "-d"):
debug_symbols.append(arg)
elif opt in ("--strict", "-s"):
strict = True
elif opt in ("--symbol"):
if not symbols:
symbols = [arg]
else:
symbols.append(arg)
elif opt in ("--duration"):
try:
duration = int(arg)
except ValueError:
print(
f"Error, duration parameters must be positive and not {duration}"
)
sys.exit(0)
if duration <= 0:
print(
f"Error, duration parameters must be positive and not {duration}"
)
sys.exit(0)

for arg in args:
backtester.backtest(
arg, conf_dict, debug_symbols, strict, symbols, duration
)
except getopt.GetoptError as e:
print(f"Error parsing options:{e}\n")
backtester.show_usage()
sys.exit(0)
elif sys.argv[1] == "from":
if sys.argv[1] == "from":
from_date = dateFromString(sys.argv[2])
tlog(f"selected {sys.argv[2]} applied {from_date}")
try:
Expand Down Expand Up @@ -189,7 +168,7 @@ if __name__ == "__main__":
sys.exit(0)
except getopt.GetoptError as e:
print(f"Error parsing options:{e}\n")
backtester.show_usage()
show_usage()
sys.exit(0)

enhanced_backtest.backtest(
Expand All @@ -205,7 +184,7 @@ if __name__ == "__main__":
)
else:
print(f"Error parsing {sys.argv[1:]}\n")
backtester.show_usage()
show_usage()
sys.exit(0)

sys.exit(0)
Loading

0 comments on commit d00ab72

Please sign in to comment.