mirror of
https://github.com/asavinov/intelligent-trading-bot.git
synced 2026-05-04 16:26:44 +00:00
152 lines
5.7 KiB
Python
152 lines
5.7 KiB
Python
from pathlib import Path
|
|
import click
|
|
from tqdm import tqdm
|
|
|
|
import numpy as np
|
|
import pandas as pd
|
|
|
|
from sklearn.metrics import (precision_recall_curve, PrecisionRecallDisplay, RocCurveDisplay)
|
|
from sklearn.model_selection import ParameterGrid
|
|
|
|
from service.App import *
|
|
from common.classifiers import *
|
|
from common.label_generation_topbot import *
|
|
from common.signal_generation import *
|
|
|
|
"""
|
|
Use predictions to simulate trade over the whole period based on one signal model.
|
|
The results of the trade simulation with signals and performances is stored in the output file.
|
|
The results can be used to further analyze (also visually) the selected trade strategy.
|
|
"""
|
|
|
|
class P:
|
|
in_nrows = 100_000_000
|
|
|
|
start_index = 0 # 200_000 for 1m btc
|
|
end_index = None
|
|
|
|
|
|
@click.command()
|
|
@click.option('--config_file', '-c', type=click.Path(), default='', help='Configuration file name')
|
|
def main(config_file):
|
|
"""
|
|
"""
|
|
load_config(config_file)
|
|
|
|
time_column = App.config["time_column"]
|
|
|
|
now = datetime.now()
|
|
|
|
symbol = App.config["symbol"]
|
|
data_path = Path(App.config["data_folder"]) / symbol
|
|
if not data_path.is_dir():
|
|
print(f"Data folder does not exist: {data_path}")
|
|
return
|
|
out_path = Path(App.config["data_folder"]) / symbol
|
|
out_path.mkdir(parents=True, exist_ok=True) # Ensure that folder exists
|
|
|
|
#
|
|
# Load data with (rolling) label point-wise predictions
|
|
#
|
|
file_path = (data_path / App.config.get("predict_file_name")).with_suffix(".csv")
|
|
if not file_path.exists():
|
|
print(f"ERROR: Input file does not exist: {file_path}")
|
|
return
|
|
|
|
print(f"Loading predictions from input file: {file_path}")
|
|
df = pd.read_csv(file_path, parse_dates=[time_column], nrows=P.in_nrows)
|
|
print(f"Predictions loaded. Length: {len(df)}. Width: {len(df.columns)}")
|
|
|
|
# Limit size according to parameters start_index end_index
|
|
df = df.iloc[P.start_index:P.end_index]
|
|
df = df.reset_index(drop=True)
|
|
|
|
#
|
|
# Find maximum performance possible based on true labels only
|
|
#
|
|
# Best parameters (just to compute for known parameters)
|
|
#df['buy_signal_column'] = score_to_signal(df[bot_score_column], None, 5, 0.09)
|
|
#df['sell_signal_column'] = score_to_signal(df[top_score_column], None, 10, 0.064)
|
|
#performance_long, performance_short, long_count, short_count, long_profitable, short_profitable, longs, shorts = performance_score(df, 'sell_signal_column', 'buy_signal_column', 'close')
|
|
# TODO: Save maximum performance in output file or print it (use as a reference)
|
|
|
|
# Maximum possible on labels themselves
|
|
#performance_long, performance_short, long_count, short_count, long_profitable, short_profitable, longs, shorts = performance_score(df, 'top10_2', 'bot10_2', 'close')
|
|
|
|
#
|
|
# Optimization: Compute averages which will be the same for all hyper-parameters
|
|
#
|
|
buy_labels = App.config["buy_labels"]
|
|
sell_labels = App.config["sell_labels"]
|
|
if set(buy_labels + sell_labels) - set(df.columns):
|
|
missing_labels = list(set(buy_labels + sell_labels) - set(df.columns))
|
|
print(f"ERROR: Some buy/sell labels from config are not present in the input data. Missing labels: {missing_labels}")
|
|
return
|
|
|
|
buy_score_column_avg = 'buy_score_column_avg'
|
|
sell_score_column_avg = 'sell_score_column_avg'
|
|
|
|
df[buy_score_column_avg] = df[buy_labels].mean(skipna=True, axis=1)
|
|
df[sell_score_column_avg] = df[sell_labels].mean(skipna=True, axis=1)
|
|
|
|
#
|
|
# Add two pairs of columns: buy_score_column/sell_score_column and buy_signal_column/sell_signal_column
|
|
#
|
|
model = App.config["signal_model"]
|
|
generate_signal_columns(df, model, buy_score_column_avg, sell_score_column_avg)
|
|
|
|
#
|
|
# Simulate trade using close price and two boolean signals
|
|
# Add a pair of two dicts: performance dict and model parameters dict
|
|
#
|
|
performance, long_performance, short_performance = \
|
|
simulated_trade_performance(df, 'sell_signal_column', 'buy_signal_column', 'close')
|
|
|
|
#
|
|
# Convert to columns: longs, shorts, signal, profit (both short and long)
|
|
#
|
|
long_df = pd.DataFrame(long_performance.get("transactions")).set_index(0, drop=True)
|
|
short_df = pd.DataFrame(short_performance.get("transactions")).set_index(0, drop=True)
|
|
df["buy_signal"] = False
|
|
df["sell_signal"] = False
|
|
df["signal"] = None
|
|
|
|
df.loc[long_df.index, "buy_signal"] = True
|
|
df.loc[long_df.index, "signal"] = "BUY"
|
|
df.loc[short_df.index, "sell_signal"] = True
|
|
df.loc[short_df.index, "signal"] = "SELL"
|
|
|
|
df["profit_long_percent"] = 0.0
|
|
df["profit_short_percent"] = 0.0
|
|
df["profit_percent"] = 0.0
|
|
df.update(short_df[4].rename("profit_long_percent"))
|
|
df.update(long_df[4].rename("profit_short_percent"))
|
|
|
|
df.update(short_df[4].rename("profit_percent"))
|
|
df.update(long_df[4].rename("profit_percent"))
|
|
|
|
# TODO: Include true labels and performance/signals on true labels
|
|
|
|
#
|
|
# Store
|
|
#
|
|
out_columns = [
|
|
"timestamp", "open", "high", "low", "close",
|
|
"buy_score_column_avg", "sell_score_column_avg",
|
|
"buy_score_column", "sell_score_column", "buy_signal_column", "sell_signal_column",
|
|
"buy_signal", "sell_signal", "signal", "profit_long_percent", "profit_short_percent", "profit_percent"
|
|
]
|
|
out_df = df[out_columns]
|
|
|
|
out_path = data_path / App.config.get("signal_file_name")
|
|
|
|
print(f"Storing output file...")
|
|
out_df.to_csv(out_path.with_suffix(".csv"), index=False, float_format='%.2f')
|
|
print(f"Signals stored in file: {out_path}. Length: {len(out_df)}. Columns: {len(out_df.columns)}")
|
|
|
|
elapsed = datetime.now() - now
|
|
print(f"Finished signal generation in {str(elapsed).split('.')[0]}")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|