def main(pipeline: str, dataset: str, symbol: str, window: int): ds = DatasetService() ms = ModelService() ts = TradingService() ohlcv_ds = ds.get_dataset('ohlcv', symbol=symbol) ohlcv = ds.get_dataset_features( ohlcv_ds) # [ohlcv_ds.valid_index_min:ohlcv_ds.valid_index_max] # boll = pd.Series(percent_b(ohlcv.close, 21), index=ohlcv.index) boll = pd.Series(to_discrete_double(percent_b(ohlcv.close, 21), 20, 80), index=ohlcv.index).replace(to_replace=-1, value=np.nan) #model = ms.get_model(pipeline, dataset, 'class', symbol) _test = ms.get_test(pipeline, dataset, 'class', symbol, window) for test in [ _test ]: # I originally traded all the tests in the model. ToDo: Refactor this. # Re-convert classification results from test to a DataFrame ohlcv_results = ohlcv[test.test_interval.begin:test.test_interval.end] results = ModelService.parse_test_results(test) #results.index = ohlcv_results.index # Parse index so it's a DateTimeIndex, because Mongo stores it as a string # results.index = pd.to_datetime(results.index) asset = ts.get_asset(pipeline=pipeline, dataset=dataset, target='class', symbol=symbol, window=test.window['days']) # Now use classification results to trade! day_count = results.shape[0] cur_day = 0 print( "%B_Precision = {}", precision_score(results.label, boll.loc[results.index], average='macro', zero_division=0)) # Amount to buy in coins for buy and hold: $10k divided by first price in test set bh_price = ohlcv.close.loc[test.test_interval.begin] bh_amount = 10000 / bh_price for index, pred in results.iterrows(): cur_day += 1 # Get simulation day by converting Pandas' Timestamp to our format simulation_day = to_timestamp(index.to_pydatetime()) # Results dataframe interprets values as float, while they are actually int predicted, label = int(pred.predicted), int(pred.label) # Grab ohlcv values for current day try: values = ohlcv.loc[index] except KeyError: print(f"Day: {index} not in OHLCV index!") continue try: boll_sig = boll.loc[ index] if boll.loc[index] != np.nan else None except KeyError: boll_sig = None print(f"Day: {index} not in BOLL index!") pass _index = ohlcv.index.get_loc(index) change = TradingService.get_percent_change(values.close, values.open) print( f"Day {cur_day}/{day_count} [{index}] " f"[O {values.open} H {values.high} L {values.low} C {values.close}] " f"PCT={change}% " f"LABEL={TARGETS[label]} BPRED={TARGETS[boll_sig]} PRED={TARGETS[predicted]}" ) open_positions = ts.get_open_positions(asset=asset, day=simulation_day) for p in open_positions: p_age = TradingService.get_position_age(position=p, day=simulation_day) try: if p.type == 'MARGIN_LONG': if TradingService.check_stop_loss(p, values.low): ts.close_long(asset=asset, day=simulation_day, close_price=p.stop_loss, position=p, detail='Stop Loss') elif TradingService.check_take_profit(p, values.high): ts.close_long(asset=asset, day=simulation_day, close_price=p.take_profit, position=p, detail='Take Profit') elif predicted == SELL: ts.close_long(asset=asset, day=simulation_day, close_price=values.close, position=p, detail='Sell Signal') elif predicted == HOLD and p_age > 86400 * 3: ts.close_long(asset=asset, day=simulation_day, close_price=values.close, position=p, detail='Age') elif predicted == BUY: if change > 0: ts.update_stop_loss(asset=asset, position=p, close_price=values.close, pct=-0.05) elif p.type == 'MARGIN_SHORT': if TradingService.check_stop_loss(p, values.high): ts.close_short(asset=asset, day=simulation_day, close_price=p.stop_loss, position=p, detail='Stop Loss') elif TradingService.check_take_profit(p, values.low): ts.close_short(asset=asset, day=simulation_day, close_price=p.take_profit, position=p, detail='Take Profit') elif predicted == SELL: # If we had some profit and signal is still SELL, book those by lowering stop loss if change < 0: ts.update_stop_loss(asset=asset, position=p, close_price=values.close, pct=0.05) elif predicted == HOLD and p_age > 86400 * 3: ts.close_short(asset=asset, day=simulation_day, close_price=values.close, position=p, detail='Age') elif predicted == BUY: ts.close_short(asset=asset, day=simulation_day, close_price=values.close, position=p, detail='Buy Signal') except MessageException as e: print(f"Order handling exception: {e.message}") try: # If prediction is BUY (price will rise) then open a MARGIN LONG position if predicted == BUY: ts.open_long(asset=asset, day=simulation_day, close_price=values.close, size=0.1, stop_loss=-0.1, take_profit=0.05) # If prediction is SELL (price will drop) open a MARGIN SHORT position elif predicted == SELL: ts.open_short(asset=asset, day=simulation_day, close_price=values.close, size=0.1, stop_loss=0.1, take_profit=-0.05) except MessageException as e: print(f"Order placement exception: {e.message}") # If this is the last trading day of the period, close all open positions if index.timestamp() == results.index[-1].timestamp(): print("Last trading day reached, liquidating all positions..") open_positions = ts.get_open_positions(asset=asset, day=simulation_day) for p in open_positions: try: if p.type == 'MARGIN_LONG': ts.close_long(asset=asset, day=simulation_day, close_price=values.close, position=p, detail='Liquidation') elif p.type == 'MARGIN_SHORT': ts.close_short(asset=asset, day=simulation_day, close_price=values.close, position=p, detail='Liquidation') except MessageException as e: print(f"Order liquidation exception: {e.message}") # Update equity value for the asset ts.update_equity(asset=asset, day=simulation_day, price=values.close) # Update baseline values for the asset ts.update_baseline(asset=asset, day=simulation_day, name='buy_and_hold', value=values.close * bh_amount) print("Timeframe done.")
def main(pipeline: str, dataset: str, symbol: str, window: int): ds = DatasetService() ms = ModelService() ts = TradingService() ohlcv_ds = ds.get_dataset('ohlcv', symbol=symbol) asset = ts.get_asset(pipeline=pipeline, dataset=dataset, target='class', symbol=symbol, window=window, create=False) if not asset: print( f"Asset {pipeline}.{dataset}.class for {symbol} on window {window} not found!" ) return test = ms.get_test(pipeline=pipeline, dataset=dataset, target='class', symbol=symbol, window=window) if not test: print( f"Test {pipeline}.{dataset}.class for {symbol} on window {window} not found!" ) # ohlcv = ohlcv.loc[test.test_interval.begin:test.test_interval.end] ohlcv = ds.get_dataset_features(ohlcv_ds, begin=test.test_interval.begin, end=test.test_interval.end) test_results = ModelService.parse_test_results(test).iloc[:-1] enc_label = onehot_target(test_results.label, labels=["is_sell", "is_hold", "is_buy"], fill=False) enc_pred = onehot_target(test_results.predicted, labels=["is_sell", "is_hold", "is_buy"], fill=False) # Mask predictions with low value minus a certain amount signals_level_diff = ohlcv.low * 10 / 100 signals_level = ohlcv.low - signals_level_diff #signals_level = ohlcv.low enc_pred.is_sell.mask(enc_pred.is_sell > 0, other=signals_level, inplace=True) enc_pred.is_hold.mask(enc_pred.is_hold > 0, other=signals_level, inplace=True) enc_pred.is_buy.mask(enc_pred.is_buy > 0, other=signals_level, inplace=True) # Get unique years in index to split plots in smaller scale unique_years = ohlcv.index.year.unique() for year in unique_years: year_pred = enc_pred[enc_pred.index.year == year] year_ohlcv = ohlcv[ohlcv.index.year == year] # Set up xticks daysToIndex = { ts.to_pydatetime(): i for i, ts in enumerate(year_ohlcv.index) } days = [i for i in daysToIndex.values()] labels = [ ts.to_pydatetime().strftime("%Y-%m-%d") for ts in year_ohlcv.index ] # Setup matplotfinance styles and figure s = mpf.make_mpf_style( base_mpf_style='binance') # , rc={'font.size': 6} fig = mpf.figure( figsize=(16, 8), style=s) # pass in the self defined style to the whole canvas fig.suptitle(f"{ohlcv_ds.symbol}, {year}, 1D") ax = fig.add_subplot(3, 1, (1, 2)) # main candle stick chart subplot av = fig.add_subplot(3, 1, 3, sharex=ax) # volume candles subplot # Setup horizontal grids ax.grid(axis='x', color='0.5', linestyle='--') av.grid(axis='x', color='0.5', linestyle='--') # for a in [ax, av]: # a.set_xticks(ticks=days) # a.set_xticklabels(labels=labels) # a.tick_params(axis='x', labelrotation=90) apds = [ # mpf.make_addplot(tcdf) # Predictions mpf.make_addplot(year_ohlcv.close, ax=ax, type='line', color=(0.5, 0.5, 0.5, 0.05)), mpf.make_addplot(year_pred.is_sell, ax=ax, type='scatter', marker='v', color='red'), mpf.make_addplot(year_pred.is_hold, ax=ax, type='scatter', marker='_', color='silver'), mpf.make_addplot(year_pred.is_buy, ax=ax, type='scatter', marker='^', color='lime'), ] mpf.plot( year_ohlcv, type='candle', style=s, #ylabel='Price ($)', ax=ax, volume=av, #ylabel_lower='Volume', show_nontrading=True, addplot=apds, returnfig=True) fig.autofmt_xdate() fig.tight_layout() plt.show() print("Done")
def main(dataset: str): ds = DatasetService() ms = ModelService() ts = TradingService() logs = [] for pipeline in PIPELINES: for symbol in SYMBOLS: for window in WINDOWS: print( f"PIPELINE: {pipeline} SYMBOL: {symbol} WINDOW: {window}") ohlcv_ds = ds.get_dataset('ohlcv', symbol=symbol) test = ms.get_test(pipeline=pipeline, dataset=dataset, target='class', symbol=symbol, window=window) if not test: print( f"Test {pipeline}.{dataset}.class for {symbol} on window {window} not found!" ) logs.append( f"MISSING_TEST {pipeline} {dataset} {symbol} class {window} --features importances_shap --parameters gridsearch\n" ) continue asset = ts.get_asset(pipeline=pipeline, dataset=dataset, target='class', symbol=symbol, window=window, create=False) if not asset: print( f"Asset {pipeline}.{dataset}.class for {symbol} on window {window} not found!" ) logs.append( f"MISSING_ASSET {pipeline} {dataset} {symbol} {window}\n" ) continue equity = TradingService.parse_equity_df(asset=asset) buy_and_hold = TradingService.parse_baseline_df( asset=asset, name='buy_and_hold') orders = TradingService.parse_orders_df(asset=asset) # Map order position_id to numbers so we don't get a mess in the graph position_uids = set(orders.position_id.values) for i, uid in enumerate(position_uids): orders.position_id.replace(to_replace=uid, value=i, inplace=True) ohlcv = ds.get_dataset_features(ohlcv_ds, begin=test.test_interval.begin, end=test.test_interval.end) test_results = ModelService.parse_test_results(test).iloc[:-1] # Mask predictions with low value minus a certain amount signals_level_diff = ohlcv.low * 10 / 100 signals_level = ohlcv.low - signals_level_diff enc_pred = onehot_target( test_results.predicted, labels=["is_sell", "is_hold", "is_buy"], fill=False) # In case of classifier bias (due to input bias) some classes are ignored. # In such cases, enc_pred won't contain the ignored classes. # Add them back by nan-filling (never selected) if hasattr(enc_pred, 'is_sell'): use_idx = enc_pred.is_sell > 0 enc_pred.is_sell.mask( use_idx, other=signals_level.loc[enc_pred.index], inplace=True) else: enc_pred['is_sell'] = pd.Series(np.nan, index=enc_pred.index) if hasattr(enc_pred, 'is_hold'): enc_pred.is_hold.mask( enc_pred.is_hold > 0, other=signals_level.loc[enc_pred.index], inplace=True) else: enc_pred['is_hold'] = pd.Series(np.nan, index=enc_pred.index) if hasattr(enc_pred, 'is_buy'): enc_pred.is_buy.mask( enc_pred.is_buy > 0, other=signals_level.loc[enc_pred.index], inplace=True) else: enc_pred['is_buy'] = pd.Series(np.nan, index=enc_pred.index) # Get unique years in index to split plots in smaller scale unique_years = ohlcv.index.year.unique() for year in unique_years: year_ohlcv = ohlcv[ohlcv.index.year == year] year_pred = enc_pred[enc_pred.index.year == year] year_equity = equity[equity.index.year == year] year_buy_and_hodl = buy_and_hold[buy_and_hold.index.year == year] year_orders = orders[orders.index.year == year] unique_quarters = year_ohlcv.index.quarter.unique() for quarter in unique_quarters: q_ohlcv = year_ohlcv[year_ohlcv.index.quarter == quarter] q_pred = year_pred[year_pred.index.quarter == quarter] q_equity = year_equity[year_equity.index.quarter == quarter] q_orders = year_orders[year_orders.index.quarter == quarter] q_buy_and_hodl = year_buy_and_hodl[ year_buy_and_hodl.index.quarter == quarter] #f"{ohlcv_ds.symbol}, {year} - Q{quarter}, 1D", 'Trades', 'Equity' img_path = f"images/backtests-final/{pipeline}-{dataset}-class-W{window}/{symbol}/" img_name = f"trades-{year}-Q{quarter}.png" if os.path.exists(f"{img_path}/{img_name}"): print(f"[SKIP] File exists {img_path}/{img_name}") continue make_plot( ohlcv=q_ohlcv, orders=q_orders, equity=q_equity, baselines=[('Buy and Hold', q_buy_and_hodl)], pred=q_pred, signals_title= f"{ohlcv_ds.symbol}, {pipeline}, W={window}D, {year} - Q{quarter}, 1D", img_path=img_path, img_name=img_name, bollinger=True) print( f"{year}-Q{quarter} saved to {img_path}{img_name}") with open(f"trading_plotly.{dataset}.log", "w") as f: f.writelines(logs) print("Logs saved")