anaucoin commited on
Commit
79189e5
1 Parent(s): 873d4cd

new app files and archive old

Browse files
Files changed (3) hide show
  1. app.py +250 -412
  2. history.csv +104 -0
  3. old_app.py +731 -0
app.py CHANGED
@@ -167,8 +167,7 @@ def cc_coding(row):
167
  return ['background-color: lightgrey'] * len(row) if row['Exit Date'] <= datetime.strptime('2022-12-16 00:00:00','%Y-%m-%d %H:%M:%S').date() else [''] * len(row)
168
  def ctt_coding(row):
169
  return ['background-color: lightgrey'] * len(row) if row['Exit Date'] <= datetime.strptime('2023-01-02 00:00:00','%Y-%m-%d %H:%M:%S').date() else [''] * len(row)
170
- def conditional_formatter(value):
171
- return "${:.2f}".format(value) if not (abs(value) < 1.00) else "${:.4f}".format(value)
172
  @st.experimental_memo
173
  def my_style(v, props=''):
174
  props = 'color:red' if v < 0 else 'color:green'
@@ -180,118 +179,51 @@ def filt_df(df, cheader, symbol_selections):
180
  df = df[df[cheader].isin(symbol_selections)]
181
 
182
  return df
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
 
184
- def tv_reformat(close50filename):
185
- try:
186
- data = pd.read_csv(open(close50filename,'r'), sep='[,|\t]', engine='python')
187
- except:
188
- data = pd.DataFrame([])
189
-
190
- if data.empty:
191
- return data
192
- else:
193
- entry_df = data[data['Type'].str.contains("Entry")]
194
- exit_df = data[data['Type'].str.contains("Exit")]
195
-
196
- entry_df.index = range(len(entry_df))
197
- exit_df.index = range(len(exit_df))
198
 
199
- df = pd.DataFrame([], columns=['Trade','Entry Date','Buy Price', 'Sell Price','Exit Date', 'P/L per token', 'P/L %', 'Drawdown %'])
200
-
201
- df['Signal'] = [string.split(' ')[1] for string in entry_df['Type']]
202
- df['Trade'] = entry_df.index
203
- df['Entry Date'] = entry_df['Date/Time']
204
- df['Buy Price'] = entry_df['Price USDT']
205
-
206
- df['Sell Price'] = exit_df['Price USDT']
207
- df['Exit Date'] = exit_df['Date/Time']
208
- df['P/L per token'] = df['Sell Price'] - df['Buy Price']
209
- df['P/L %'] = exit_df['Profit %']
210
- df['Drawdown %'] = exit_df['Drawdown %']
211
- df['Close 50'] = [int(i == "Close 50% of Position") for i in exit_df['Signal']]
212
- df = df.sort_values(['Entry Date','Close 50'], ascending = [False, True])
213
- df.index = range(len(df))
214
-
215
- df.loc[df['Close 50'] == 1, 'Exit Date'] = np.copy(df.loc[df[df['Close 50'] == 1].index.values -1]['Exit Date'])
216
-
217
- grouped_df = df.groupby('Entry Date').agg({'Signal' : 'first', 'Entry Date': 'min', 'Buy Price':'mean',
218
- 'Sell Price' : 'mean',
219
- 'Exit Date': 'max',
220
- 'P/L per token': 'mean',
221
- 'P/L %' : 'mean'})
222
-
223
- grouped_df.insert(0,'Trade', range(len(grouped_df)))
224
- grouped_df.index = range(len(grouped_df))
225
- return grouped_df
226
-
227
- def load_data(filename, otimeheader, fmat):
228
- df = pd.read_csv(open(filename,'r'), sep='\t') # so as not to mutate cached value
229
- close50filename = filename.split('.')[0] + '-50.' + filename.split('.')[1]
230
- df2 = tv_reformat(close50filename)
231
-
232
- if filename == "CT-Trade-Log.csv":
233
- df.columns = ['Trade','Entry Date','Buy Price', 'Sell Price','Exit Date', 'P/L per token', 'P/L %', 'Drawdown %']
234
- df.insert(1, 'Signal', ['Long']*len(df))
235
- elif filename == "CC-Trade-Log.csv" or "PB-Trade-Log.csv":
236
- df.columns = ['Trade','Signal','Entry Date','Buy Price', 'Sell Price','Exit Date', 'P/L per token', 'P/L %', 'Drawdown %']
237
- else:
238
- df.columns = ['Trade','Signal','Entry Date','Buy Price', 'Sell Price','Exit Date', 'P/L per token', 'P/L %']
239
-
240
- if filename != "CT-Toasted-Trade-Log.csv":
241
- df['Signal'] = df['Signal'].str.replace(' ', '', regex=True)
242
- df['Buy Price'] = df['Buy Price'].str.replace('$', '', regex=True)
243
- df['Sell Price'] = df['Sell Price'].str.replace('$', '', regex=True)
244
- df['Buy Price'] = df['Buy Price'].str.replace(',', '', regex=True)
245
- df['Sell Price'] = df['Sell Price'].str.replace(',', '', regex=True)
246
- df['P/L per token'] = df['P/L per token'].str.replace('$', '', regex=True)
247
- df['P/L per token'] = df['P/L per token'].str.replace(',', '', regex=True)
248
- df['P/L %'] = df['P/L %'].str.replace('%', '', regex=True)
249
-
250
- df['Buy Price'] = pd.to_numeric(df['Buy Price'])
251
- df['Sell Price'] = pd.to_numeric(df['Sell Price'])
252
- df['P/L per token'] = pd.to_numeric(df['P/L per token'])
253
- df['P/L %'] = pd.to_numeric(df['P/L %'])
254
-
255
- if df2.empty:
256
- df = df
257
- else:
258
- df = pd.concat([df,df2], axis=0, ignore_index=True)
259
-
260
- if filename == "CT-Trade-Log.csv":
261
- df['Signal'] = ['Long']*len(df)
262
-
263
  dateheader = 'Date'
264
  theader = 'Time'
 
 
 
265
 
266
- df[dateheader] = [tradetimes.split(" ")[0] for tradetimes in df[otimeheader].values]
267
- df[theader] = [tradetimes.split(" ")[1] for tradetimes in df[otimeheader].values]
268
-
269
- df[otimeheader]= [dateutil.parser.parse(date+' '+time)
270
- for date,time in zip(df[dateheader],df[theader])]
271
- df[otimeheader] = pd.to_datetime(df[otimeheader])
272
- df['Exit Date'] = pd.to_datetime(df['Exit Date'])
273
- df.sort_values(by=otimeheader, inplace=True)
274
 
275
- df[dateheader] = [dateutil.parser.parse(date).date() for date in df[dateheader]]
276
- df[theader] = [dateutil.parser.parse(time).time() for time in df[theader]]
277
- df['Trade'] = df.index + 1 #reindex
278
 
279
- if filename == "CT-Trade-Log.csv":
280
- df['DCA'] = np.nan
281
-
282
- for exit in pd.unique(df['Exit Date']):
283
- df_exit = df[df['Exit Date']==exit]
284
- if dateutil.parser.parse(str(exit)) < dateutil.parser.parse('2023-02-07 13:00:00'):
285
- for i in range(len(df_exit)):
286
- ind = df_exit.index[i]
287
- df.loc[ind,'DCA'] = i+1
288
-
289
- else:
290
- for i in range(len(df_exit)):
291
- ind = df_exit.index[i]
292
- df.loc[ind,'DCA'] = i+1.1
293
- return df
294
-
295
 
296
  def get_sd_df(sd_df, sd, bot_selections, dca1, dca2, dca3, dca4, dca5, dca6, fees, lev, dollar_cap, principal_balance):
297
  sd = 2*.00026
@@ -349,43 +281,27 @@ def get_sd_df(sd_df, sd, bot_selections, dca1, dca2, dca3, dca4, dca5, dca6, fee
349
  return sd_df
350
 
351
  def runapp() -> None:
352
- bot_selections = "Pure Bread"
353
  otimeheader = 'Exit Date'
354
  fmat = '%Y-%m-%d %H:%M:%S'
355
  fees = .075/100
356
 
357
- st.header(f"{bot_selections} Performance Dashboard :bread: :moneybag:")
358
  no_errors = True
359
- st.write("Welcome to the Trading Bot Dashboard by BreadBytes! You can use this dashboard to track " +
360
- "the performance of our trading bots.")
361
 
362
- if bot_selections == "Cinnamon Toast":
363
  lev_cap = 5
364
  dollar_cap = 1000000000.00
365
- data = load_data("CT-Trade-Log.csv",otimeheader, fmat)
366
- if bot_selections == "French Toast":
367
- lev_cap = 3
368
- dollar_cap = 10000000000.00
369
- data = load_data("FT-Trade-Log.csv",otimeheader, fmat)
370
- if bot_selections == "Short Bread":
371
- lev_cap = 5
372
- dollar_cap = 1000000000.00
373
- data = load_data("SB-Trade-Log.csv",otimeheader, fmat)
374
- if bot_selections == "Cosmic Cupcake":
375
- lev_cap = 3
376
- dollar_cap = 1000000000.00
377
- data = load_data("CC-Trade-Log.csv",otimeheader, fmat)
378
- if bot_selections == "Pure Bread":
379
- lev_cap = 3
380
- dollar_cap = 1000000000.00
381
- data = load_data("PB-Trade-Log.csv",otimeheader, fmat)
382
 
383
  df = data.copy(deep=True)
384
 
385
  dateheader = 'Date'
386
  theader = 'Time'
387
 
388
- st.subheader("Choose your settings:")
389
  with st.form("user input", ):
390
  if no_errors:
391
  with st.container():
@@ -413,310 +329,228 @@ def runapp() -> None:
413
  lev = st.number_input('Leverage', min_value=1, value=1, max_value= lev_cap, step=1)
414
  with col1:
415
  principal_balance = st.number_input('Starting Balance', min_value=0.00, value=1000.00, max_value= dollar_cap, step=.01)
416
-
417
- if bot_selections == "Cinnamon Toast":
418
- st.write("Choose your DCA setup (for trades before 02/07/2023)")
419
- with st.container():
420
- col1, col2, col3, col4 = st.columns(4)
421
- with col1:
422
- dca1 = st.number_input('DCA 1 Allocation', min_value=0, value=25, max_value= 100, step=1)
423
- with col2:
424
- dca2 = st.number_input('DCA 2 Allocation', min_value=0, value=25, max_value= 100, step=1)
425
- with col3:
426
- dca3 = st.number_input('DCA 3 Allocation', min_value=0, value=25, max_value= 100, step=1)
427
- with col4:
428
- dca4 = st.number_input('DCA 4 Allocation', min_value=0, value=25, max_value= 100, step=1)
429
- st.write("Choose your DCA setup (for trades on or after 02/07/2023)")
430
- with st.container():
431
- col1, col2 = st.columns(2)
432
- with col1:
433
- dca5 = st.number_input('DCA 1 Allocation', min_value=0, value=50, max_value= 100, step=1)
434
- with col2:
435
- dca6 = st.number_input('DCA 2 Allocation', min_value=0, value=50, max_value= 100, step=1)
436
 
437
  #hack way to get button centered
438
  c = st.columns(9)
439
  with c[4]:
440
  submitted = st.form_submit_button("Get Cookin'!")
441
-
442
  if submitted and principal_balance * lev > dollar_cap:
443
  lev = np.floor(dollar_cap/principal_balance)
444
  st.error(f"WARNING: (Starting Balance)*(Leverage) exceeds the ${dollar_cap} limit. Using maximum available leverage of {lev}")
445
 
446
- if submitted and no_errors:
447
- df = df[(df[dateheader] >= startdate) & (df[dateheader] <= enddate)]
448
- signal_map = {'Long': 1, 'Short':-1}
449
-
450
-
451
- if len(df) == 0:
452
- st.error("There are no available trades matching your selections. Please try again!")
453
- no_errors = False
454
-
455
- if no_errors:
456
- if bot_selections == "Cinnamon Toast":
457
- dca_map = {1: dca1/100, 2: dca2/100, 3: dca3/100, 4: dca4/100, 1.1: dca5/100, 2.1: dca6/100}
458
- df['DCA %'] = df['DCA'].map(dca_map)
459
- df['Calculated Return %'] = df['Signal'].map(signal_map)*(df['DCA %'])*(1-fees)*((df['Sell Price']-df['Buy Price'])/df['Buy Price'] - fees) #accounts for fees on open and close of trade
460
- df['DCA'] = np.floor(df['DCA'].values)
461
-
462
- df['Return Per Trade'] = np.nan
463
- df['Balance used in Trade'] = np.nan
464
- df['New Balance'] = np.nan
465
-
466
- g = df.groupby('Exit Date').sum(numeric_only=True)['Calculated Return %'].reset_index(name='Return Per Trade')
467
- df.loc[df['DCA']==1.0,'Return Per Trade'] = 1+lev*g['Return Per Trade'].values
468
-
469
- df['Compounded Return'] = df['Return Per Trade'].cumprod()
470
- df.loc[df['DCA']==1.0,'New Balance'] = [min(dollar_cap/lev, bal*principal_balance) for bal in df.loc[df['DCA']==1.0,'Compounded Return']]
471
- df.loc[df['DCA']==1.0,'Balance used in Trade'] = np.concatenate([[principal_balance], df.loc[df['DCA']==1.0,'New Balance'].values[:-1]])
472
- else:
473
- df['Calculated Return %'] = df['Signal'].map(signal_map)*(1-fees)*((df['Sell Price']-df['Buy Price'])/df['Buy Price'] - fees) #accounts for fees on open and close of trade
474
- df['Return Per Trade'] = np.nan
475
- g = df.groupby('Exit Date').sum(numeric_only=True)['Calculated Return %'].reset_index(name='Return Per Trade')
476
- df['Return Per Trade'] = 1+lev*g['Return Per Trade'].values
477
-
478
- df['Compounded Return'] = df['Return Per Trade'].cumprod()
479
- df['New Balance'] = [min(dollar_cap/lev, bal*principal_balance) for bal in df['Compounded Return']]
480
- df['Balance used in Trade'] = np.concatenate([[principal_balance], df['New Balance'].values[:-1]])
481
- df['Net P/L Per Trade'] = (df['Return Per Trade']-1)*df['Balance used in Trade']
482
- df['Cumulative P/L'] = df['Net P/L Per Trade'].cumsum()
483
-
484
- if bot_selections == "Cinnamon Toast" or bot_selections == "Cosmic Cupcake" or bot_selections == "Pure Bread":
485
- cum_pl = df.loc[df.drop('Drawdown %', axis=1).dropna().index[-1],'Cumulative P/L'] + principal_balance
486
- #cum_sdp = sd_df.loc[sd_df.drop('Drawdown %', axis=1).dropna().index[-1],'Cumulative P/L (+)'] + principal_balance
487
- #cum_sdm = sd_df.loc[sd_df.drop('Drawdown %', axis=1).dropna().index[-1],'Cumulative P/L (-)'] + principal_balance
488
- else:
489
- cum_pl = df.loc[df.dropna().index[-1],'Cumulative P/L'] + principal_balance
490
- #cum_sdp = sd_df.loc[sd_df.dropna().index[-1],'Cumulative P/L (+)'] + principal_balance
491
- #cum_sdm = sd_df.loc[sd_df.dropna().index[-1],'Cumulative P/L (-)'] + principal_balance
492
- #sd = 2*.00026
493
- #sd_df = get_sd_df(get_sd_df(df.copy(), sd, bot_selections, dca1, dca2, dca3, dca4, dca5, dca6, fees, lev, dollar_cap, principal_balance)
494
-
495
- effective_return = 100*((cum_pl - principal_balance)/principal_balance)
496
-
497
- st.header(f"{bot_selections} Results")
498
- with st.container():
499
-
500
- if len(bot_selections) > 1:
501
- col1, col2 = st.columns(2)
502
- with col1:
503
- st.metric(
504
- "Total Account Balance",
505
- f"${cum_pl:.2f}",
506
- f"{100*(cum_pl-principal_balance)/(principal_balance):.2f} %",
507
- )
508
 
509
- # with col2:
510
- # st.write("95% of trades should fall within this 2 std. dev. range.")
511
- # st.metric(
512
- # "High Range (+ 2 std. dev.)",
513
- # f"", #${cum_sdp:.2f}
514
- # f"{100*(cum_sdp-principal_balance)/(principal_balance):.2f} %",
515
- # )
516
- # st.metric(
517
- # "Low Range (- 2 std. dev.)",
518
- # f"" ,#${cum_sdm:.2f}"
519
- # f"{100*(cum_sdm-principal_balance)/(principal_balance):.2f} %",
520
- # )
521
- if bot_selections == "Cinnamon Toast" or bot_selections == "Cosmic Cupcake" or bot_selections == "Pure Bread":
522
- #st.line_chart(data=df.drop('Drawdown %', axis=1).dropna(), x='Exit Date', y='Cumulative P/L', use_container_width=True)
523
- dfdata = df.drop('Drawdown %', axis=1).dropna()
524
- #sd_df = sd_df.drop('Drawdown %', axis=1).dropna()
525
- else:
526
- #st.line_chart(data=df.dropna(), x='Exit Date', y='Cumulative P/L', use_container_width=True)
527
- dfdata = df.dropna()
528
- #sd_df = sd_df.dropna()
529
-
530
- # Create figure
531
- fig = go.Figure()
532
-
533
- pyLogo = Image.open("logo.png")
534
-
535
  # fig.add_traces(go.Scatter(x=sd_df['Exit Date'], y = sd_df['Cumulative P/L (+)'],line_shape='spline',
536
  # line = dict(smoothing = 1.3, color='rgba(31, 119, 200,0)'), showlegend = False)
537
  # )
538
-
539
  # fig.add_traces(go.Scatter(x=sd_df['Exit Date'], y = sd_df['Cumulative P/L (-)'],
540
  # line = dict(smoothing = 1.3, color='rgba(31, 119, 200,0)'), line_shape='spline',
541
  # fill='tonexty',
542
  # fillcolor = 'rgba(31, 119, 200,.2)', name = '+/- Standard Deviation')
543
  # )
544
 
545
- # Add trace
546
- fig.add_trace(
547
- go.Scatter(x=dfdata['Exit Date'], y=np.round(dfdata['Cumulative P/L'].values,2), line_shape='spline',
548
- line = {'smoothing': 1.0, 'color' : 'rgba(31, 119, 200,.8)'},
549
- name='Cumulative P/L')
550
- )
551
- buyhold = (principal_balance/dfdata['Buy Price'][dfdata.index[0]])*(dfdata['Buy Price']-dfdata['Buy Price'][dfdata.index[0]])
552
- fig.add_trace(go.Scatter(x=dfdata['Exit Date'], y=np.round(buyhold.values,2), line_shape='spline',
553
- line = {'smoothing': 1.0, 'color' :'red'}, name = 'Buy & Hold Return')
554
- )
555
-
556
- fig.add_layout_image(
557
- dict(
558
- source=pyLogo,
559
- xref="paper",
560
- yref="paper",
561
- x = 0.05, #dfdata['Exit Date'].astype('int64').min() // 10**9,
562
- y = .85, #dfdata['Cumulative P/L'].max(),
563
- sizex= .9, #(dfdata['Exit Date'].astype('int64').max() - dfdata['Exit Date'].astype('int64').min()) // 10**9,
564
- sizey= .9, #(dfdata['Cumulative P/L'].max() - dfdata['Cumulative P/L'].min()),
565
- sizing="contain",
566
- opacity=0.2,
567
- layer = "below")
568
- )
569
-
570
- #style layout
571
- fig.update_layout(
572
- height = 600,
573
- xaxis=dict(
574
- title="Exit Date",
575
- tickmode='array',
576
- ),
577
- yaxis=dict(
578
- title="Cumulative P/L"
579
- ) )
580
-
581
- st.plotly_chart(fig, theme=None, use_container_width=True,height=600)
582
- st.write()
583
- df['Per Trade Return Rate'] = df['Return Per Trade']-1
584
-
585
- totals = pd.DataFrame([], columns = ['# of Trades', 'Wins', 'Losses', 'Win Rate', 'Profit Factor'])
586
- if bot_selections == "Cinnamon Toast" or bot_selections == "Cosmic Cupcake" or bot_selections == "Pure Bread":
587
- data = get_hist_info(df.drop('Drawdown %', axis=1).dropna(), principal_balance,'Per Trade Return Rate')
588
- else:
589
- data = get_hist_info(df.dropna(), principal_balance,'Per Trade Return Rate')
590
- totals.loc[len(totals)] = list(i for i in data)
591
-
592
- totals['Cum. P/L'] = cum_pl-principal_balance
593
- totals['Cum. P/L (%)'] = 100*(cum_pl-principal_balance)/principal_balance
594
-
595
- if df.empty:
596
- st.error("Oops! None of the data provided matches your selection(s). Please try again.")
597
- else:
598
- with st.container():
599
- for row in totals.itertuples():
600
- col1, col2, col3, col4= st.columns(4)
601
- c1, c2, c3, c4 = st.columns(4)
602
- with col1:
603
- st.metric(
604
- "Total Trades",
605
- f"{row._1:.0f}",
606
- )
607
- with c1:
608
- st.metric(
609
- "Profit Factor",
610
- f"{row._5:.2f}",
611
- )
612
- with col2:
613
- st.metric(
614
- "Wins",
615
- f"{row.Wins:.0f}",
616
- )
617
- with c2:
618
- st.metric(
619
- "Cumulative P/L",
620
- f"${row._6:.2f}",
621
- f"{row._7:.2f} %",
622
- )
623
- with col3:
624
- st.metric(
625
- "Losses",
626
- f"{row.Losses:.0f}",
627
- )
628
- with c3:
629
- st.metric(
630
- "Rolling 7 Days",
631
- "",#f"{(1+get_rolling_stats(df,otimeheader, 30))*principal_balance:.2f}",
632
- f"{get_rolling_stats(df,lev, otimeheader, 7):.2f}%",
633
- )
634
- st.metric(
635
- "Rolling 30 Days",
636
- "",#f"{(1+get_rolling_stats(df,otimeheader, 30))*principal_balance:.2f}",
637
- f"{get_rolling_stats(df,lev, otimeheader, 30):.2f}%",
638
- )
639
-
640
- with col4:
641
- st.metric(
642
- "Win Rate",
643
- f"{row._4:.1f}%",
644
- )
645
- with c4:
646
- st.metric(
647
- "Rolling 90 Days",
648
- "",#f"{(1+get_rolling_stats(df,otimeheader, 30))*principal_balance:.2f}",
649
- f"{get_rolling_stats(df,lev, otimeheader, 90):.2f}%",
650
- )
651
- st.metric(
652
- "Rolling 180 Days",
653
- "",#f"{(1+get_rolling_stats(df,otimeheader, 30))*principal_balance:.2f}",
654
- f"{get_rolling_stats(df,lev, otimeheader, 180):.2f}%",
655
- )
656
-
657
- if bot_selections == "Cinnamon Toast":
658
- if submitted:
659
- grouped_df = df.groupby('Exit Date').agg({'Signal':'min','Entry Date': 'min','Exit Date': 'max','Buy Price': 'mean',
660
- 'Sell Price' : 'max',
661
- 'Net P/L Per Trade': 'mean',
662
- 'Calculated Return %' : lambda x: np.round(100*lev*x.sum(),2),
663
- 'DCA': lambda x: int(np.floor(x.max()))})
664
- grouped_df.index = range(1, len(grouped_df)+1)
665
- grouped_df.rename(columns={'DCA' : '# of DCAs', 'Buy Price':'Avg. Buy Price',
666
- 'Net P/L Per Trade':'Net P/L',
667
- 'Calculated Return %':'P/L %'}, inplace=True)
668
  else:
669
- dca_map = {1: 25/100, 2: 25/100, 3: 25/100, 4: 25/100, 1.1: 50/100, 2.1: 50/100}
670
- df['DCA %'] = df['DCA'].map(dca_map)
671
- df['Calculated Return %'] = (df['DCA %'])*(1-fees)*((df['Sell Price']-df['Buy Price'])/df['Buy Price'] - fees) #accounts for fees on open and close of trade
672
-
673
- grouped_df = df.groupby('Exit Date').agg({'Signal':'min','Entry Date': 'min','Exit Date': 'max','Buy Price': 'mean',
674
- 'Sell Price' : 'max',
675
- 'P/L per token': 'mean',
676
- 'Calculated Return %' : lambda x: np.round(100*x.sum(),2),
677
- 'DCA': lambda x: int(np.floor(x.max()))})
678
- grouped_df.index = range(1, len(grouped_df)+1)
679
- grouped_df.rename(columns={'DCA' : '# of DCAs', 'Buy Price':'Avg. Buy Price',
680
- 'Calculated Return %':'P/L %',
681
- 'P/L per token':'Net P/L'}, inplace=True)
682
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
683
  else:
684
- if submitted:
685
- grouped_df = df.groupby('Exit Date').agg({'Signal':'min','Entry Date': 'min','Exit Date': 'max','Buy Price': 'mean',
686
- 'Sell Price' : 'max',
687
- 'Net P/L Per Trade': 'mean',
688
- 'Calculated Return %' : lambda x: np.round(100*lev*x.sum(),2)})
689
- grouped_df.index = range(1, len(grouped_df)+1)
690
- grouped_df.rename(columns={'Buy Price':'Avg. Buy Price',
691
- 'Net P/L Per Trade':'Net P/L',
692
- 'Calculated Return %':'P/L %'}, inplace=True)
693
- else:
694
- grouped_df = df.groupby('Exit Date').agg({'Signal':'min','Entry Date': 'min','Exit Date': 'max','Buy Price': 'mean',
695
- 'Sell Price' : 'max',
696
- 'P/L per token': 'mean',
697
- 'P/L %':'mean'})
698
- grouped_df.index = range(1, len(grouped_df)+1)
699
- grouped_df.rename(columns={'Buy Price':'Avg. Buy Price',
700
- 'P/L per token':'Net P/L'}, inplace=True)
701
  st.subheader("Trade Logs")
702
  grouped_df['Entry Date'] = pd.to_datetime(grouped_df['Entry Date'])
703
  grouped_df['Exit Date'] = pd.to_datetime(grouped_df['Exit Date'])
704
- if bot_selections == "Cosmic Cupcake" or bot_selections == "CT Toasted":
705
- coding = cc_coding if bot_selections == "Cosmic Cupcake" else ctt_coding
706
- st.dataframe(grouped_df.style.format({'Entry Date':'{:%m-%d-%Y %H:%M:%S}','Exit Date':'{:%m-%d-%Y %H:%M:%S}','Avg. Buy Price': '${:.2f}', 'Sell Price': '${:.2f}', 'Net P/L':'${:.2f}', 'P/L %':'{:.2f}%'})\
707
- .apply(coding, axis=1)\
708
- .applymap(my_style,subset=['Net P/L'])\
709
- .applymap(my_style,subset=['P/L %']), use_container_width=True)
710
- # new_title = '<div style="text-align: right;"><span style="background-color:lightgrey;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> Not Live Traded</div>'
711
- # st.markdown(new_title, unsafe_allow_html=True)
712
- elif bot_selections == "Pure Bread":
713
- st.dataframe(grouped_df.style.format({'Entry Date':'{:%m-%d-%Y %H:%M:%S}','Exit Date':'{:%m-%d-%Y %H:%M:%S}','Avg. Buy Price': '${:.4f}', 'Sell Price': '${:.4f}', 'Net P/L':conditional_formatter, 'P/L %':'{:.2f}%'})\
714
- .applymap(my_style,subset=['Net P/L'])\
715
- .applymap(my_style,subset=['P/L %']), use_container_width=True)
716
- else:
717
- st.dataframe(grouped_df.style.format({'Entry Date':'{:%m-%d-%Y %H:%M:%S}','Exit Date':'{:%m-%d-%Y %H:%M:%S}','Avg. Buy Price': '${:.2f}', 'Sell Price': '${:.2f}', 'Net P/L':'${:.2f}', 'P/L %':'{:.2f}%'})\
718
- .applymap(my_style,subset=['Net P/L'])\
719
- .applymap(my_style,subset=['P/L %']), use_container_width=True)
720
 
721
  # st.subheader("Checking Status")
722
  # if submitted:
@@ -724,7 +558,11 @@ def runapp() -> None:
724
 
725
  if __name__ == "__main__":
726
  st.set_page_config(
727
- "Trading Bot Dashboard",
728
- layout="wide",
729
  )
730
  runapp()
 
 
 
 
 
 
167
  return ['background-color: lightgrey'] * len(row) if row['Exit Date'] <= datetime.strptime('2022-12-16 00:00:00','%Y-%m-%d %H:%M:%S').date() else [''] * len(row)
168
  def ctt_coding(row):
169
  return ['background-color: lightgrey'] * len(row) if row['Exit Date'] <= datetime.strptime('2023-01-02 00:00:00','%Y-%m-%d %H:%M:%S').date() else [''] * len(row)
170
+
 
171
  @st.experimental_memo
172
  def my_style(v, props=''):
173
  props = 'color:red' if v < 0 else 'color:green'
 
179
  df = df[df[cheader].isin(symbol_selections)]
180
 
181
  return df
182
+ def load_data(filename, account, exchange, otimeheader, fmat):
183
+ cols = ['id','datetime', 'exchange', 'subaccount', 'pair', 'side', 'action', 'amount', 'price']
184
+ df = pd.read_csv(filename, header = 0, names= cols)
185
+
186
+ filtdf = df[(df.exchange == exchange) & (df.subaccount == account)].dropna()
187
+ filtdf = filtdf.sort_values('datetime')
188
+ filtdf = filtdf.iloc[np.where(filtdf.action == 'open')[0][0]:, :] #get first open signal in dataframe
189
+
190
+ tnum = 0
191
+ dca = 0
192
+ newdf = pd.DataFrame([], columns=['Trade','Signal','Entry Date','Buy Price', 'Sell Price','Exit Date', 'P/L per token', 'P/L %'])
193
+ for index, row in filtdf.iterrows():
194
+ if row.action == 'open':
195
+ dca += 1
196
+ tnum += 1
197
+ sig = 'Long' if row.side == 'buy' else 'Short'
198
+ temp = pd.DataFrame({'Trade' :[tnum], 'Signal': [sig], 'Entry Date':[row.datetime],'Buy Price': [row.price], 'Sell Price': [np.nan],'Exit Date': [np.nan], 'P/L per token': [np.nan], 'P/L %': [np.nan], 'DCA': [dca]})
199
+ newdf = pd.concat([newdf,temp], ignore_index = True)
200
+ if row.action == 'close':
201
+ for j in np.arange(tnum-1, tnum-dca-1,-1):
202
+ newdf.loc[j,'Sell Price'] = row.price
203
+ newdf.loc[j,'Exit Date'] = row.datetime
204
+ dca = 0
205
+
206
+ newdf['Buy Price'] = pd.to_numeric(newdf['Buy Price'])
207
+ newdf['Sell Price'] = pd.to_numeric(newdf['Sell Price'])
208
+
209
+ newdf['P/L per token'] = newdf['Sell Price'] - newdf['Buy Price']
210
+ newdf['P/L %'] = 100*newdf['P/L per token']/newdf['Buy Price']
211
+ newdf = newdf.dropna()
212
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
213
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
  dateheader = 'Date'
215
  theader = 'Time'
216
+
217
+ newdf[dateheader] = [tradetimes.split(" ")[0] for tradetimes in newdf[otimeheader].values]
218
+ newdf[theader] = [tradetimes.split(" ")[1] for tradetimes in newdf[otimeheader].values]
219
 
220
+ newdf[otimeheader] = pd.to_datetime(newdf[otimeheader])
221
+ newdf['Exit Date'] = pd.to_datetime(newdf['Exit Date'])
 
 
 
 
 
 
222
 
223
+ newdf[dateheader] = [dateutil.parser.parse(date).date() for date in newdf[dateheader]]
224
+ newdf[theader] = [dateutil.parser.parse(time).time() for time in newdf[theader]]
 
225
 
226
+ return newdf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
227
 
228
  def get_sd_df(sd_df, sd, bot_selections, dca1, dca2, dca3, dca4, dca5, dca6, fees, lev, dollar_cap, principal_balance):
229
  sd = 2*.00026
 
281
  return sd_df
282
 
283
  def runapp() -> None:
284
+ bot_selections = "Pumpernickel"
285
  otimeheader = 'Exit Date'
286
  fmat = '%Y-%m-%d %H:%M:%S'
287
  fees = .075/100
288
 
289
+ #st.header(f"{bot_selections} Performance Dashboard :bread: :moneybag:")
290
  no_errors = True
291
+ #st.write("Welcome to the Trading Bot Dashboard by BreadBytes! You can use this dashboard to track " +
292
+ # "the performance of our trading bots.")
293
 
294
+ if bot_selections == "Pumpernickel":
295
  lev_cap = 5
296
  dollar_cap = 1000000000.00
297
+ data = load_data('history.csv', 'Pumpernickel Test', 'Bybit Futures', otimeheader, fmat)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
298
 
299
  df = data.copy(deep=True)
300
 
301
  dateheader = 'Date'
302
  theader = 'Time'
303
 
304
+ #st.subheader("Choose your settings:")
305
  with st.form("user input", ):
306
  if no_errors:
307
  with st.container():
 
329
  lev = st.number_input('Leverage', min_value=1, value=1, max_value= lev_cap, step=1)
330
  with col1:
331
  principal_balance = st.number_input('Starting Balance', min_value=0.00, value=1000.00, max_value= dollar_cap, step=.01)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
332
 
333
  #hack way to get button centered
334
  c = st.columns(9)
335
  with c[4]:
336
  submitted = st.form_submit_button("Get Cookin'!")
337
+ signal_map = {'Long': 1, 'Short':-1}
338
  if submitted and principal_balance * lev > dollar_cap:
339
  lev = np.floor(dollar_cap/principal_balance)
340
  st.error(f"WARNING: (Starting Balance)*(Leverage) exceeds the ${dollar_cap} limit. Using maximum available leverage of {lev}")
341
 
342
+ df = df[(df[dateheader] >= startdate) & (df[dateheader] <= enddate)]
343
+
344
+ if submitted and len(df) == 0:
345
+ st.error("There are no available trades matching your selections. Please try again!")
346
+ no_errors = False
347
+
348
+ if no_errors:
349
+ if bot_selections == "Pumpernickel":
350
+ dca_map = {1: 1/3, 2: 1/3, 3: 1/3}
351
+ df['DCA %'] = df['DCA'].map(dca_map)
352
+ df['Calculated Return %'] = df['Signal'].map(signal_map)*(df['DCA %'])*(1-fees)*((df['Sell Price']-df['Buy Price'])/df['Buy Price'] - fees) #accounts for fees on open and close of trade
353
+ df['DCA'] = np.floor(df['DCA'].values)
354
+
355
+ df['Return Per Trade'] = np.nan
356
+ df['Balance used in Trade'] = np.nan
357
+ df['New Balance'] = np.nan
358
+
359
+ g = df.groupby('Exit Date').sum(numeric_only=True)['Calculated Return %'].reset_index(name='Return Per Trade')
360
+ df.loc[df['DCA']==1.0,'Return Per Trade'] = 1+lev*g['Return Per Trade'].values
361
+
362
+ df['Compounded Return'] = df['Return Per Trade'].cumprod()
363
+ df.loc[df['DCA']==1.0,'New Balance'] = [min(dollar_cap/lev, bal*principal_balance) for bal in df.loc[df['DCA']==1.0,'Compounded Return']]
364
+ df.loc[df['DCA']==1.0,'Balance used in Trade'] = np.concatenate([[principal_balance], df.loc[df['DCA']==1.0,'New Balance'].values[:-1]])
365
+ else:
366
+ df['Calculated Return %'] = df['Signal'].map(signal_map)*(1-fees)*((df['Sell Price']-df['Buy Price'])/df['Buy Price'] - fees) #accounts for fees on open and close of trade
367
+ df['Return Per Trade'] = np.nan
368
+ g = df.groupby('Exit Date').sum(numeric_only=True)['Calculated Return %'].reset_index(name='Return Per Trade')
369
+ df['Return Per Trade'] = 1+lev*g['Return Per Trade'].values
370
+
371
+ df['Compounded Return'] = df['Return Per Trade'].cumprod()
372
+ df['New Balance'] = [min(dollar_cap/lev, bal*principal_balance) for bal in df['Compounded Return']]
373
+ df['Balance used in Trade'] = np.concatenate([[principal_balance], df['New Balance'].values[:-1]])
374
+ df['Net P/L Per Trade'] = (df['Return Per Trade']-1)*df['Balance used in Trade']
375
+ df['Cumulative P/L'] = df['Net P/L Per Trade'].cumsum()
376
+
377
+
378
+ cum_pl = df.loc[df.dropna().index[-1],'Cumulative P/L'] + principal_balance
379
+
380
+ effective_return = 100*((cum_pl - principal_balance)/principal_balance)
381
+
382
+ #st.header(f"{bot_selections} Results")
383
+ with st.container():
384
+
385
+ if len(bot_selections) > 1:
386
+ col1, col2 = st.columns(2)
387
+ with col1:
388
+ st.metric(
389
+ "Total Account Balance",
390
+ f"${cum_pl:.2f}",
391
+ f"{100*(cum_pl-principal_balance)/(principal_balance):.2f} %",
392
+ )
393
+
394
+ dfdata = df.dropna()
395
+
396
+ # Create figure
397
+ fig = go.Figure()
398
+
399
+ pyLogo = Image.open("logo.png")
 
 
 
 
400
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
401
  # fig.add_traces(go.Scatter(x=sd_df['Exit Date'], y = sd_df['Cumulative P/L (+)'],line_shape='spline',
402
  # line = dict(smoothing = 1.3, color='rgba(31, 119, 200,0)'), showlegend = False)
403
  # )
404
+
405
  # fig.add_traces(go.Scatter(x=sd_df['Exit Date'], y = sd_df['Cumulative P/L (-)'],
406
  # line = dict(smoothing = 1.3, color='rgba(31, 119, 200,0)'), line_shape='spline',
407
  # fill='tonexty',
408
  # fillcolor = 'rgba(31, 119, 200,.2)', name = '+/- Standard Deviation')
409
  # )
410
 
411
+ # Add trace
412
+ fig.add_trace(
413
+ go.Scatter(x=dfdata['Exit Date'], y=np.round(dfdata['Cumulative P/L'].values,2), line_shape='spline',
414
+ line = {'smoothing': .7, 'color' : 'rgba(90, 223, 137, 1)'},
415
+ name='P/L')
416
+ )
417
+ buyhold = (principal_balance/dfdata['Buy Price'][dfdata.index[0]])*(dfdata['Buy Price']-dfdata['Buy Price'][dfdata.index[0]])
418
+ fig.add_trace(go.Scatter(x=dfdata['Exit Date'], y=np.round(buyhold.values,2), line_shape='spline',
419
+ line = {'smoothing': .7, 'color' :'rgba(33, 212, 225, 1)'}, name = 'Buy & Hold')
420
+ )
421
+
422
+ fig.add_layout_image(
423
+ dict(
424
+ source=pyLogo,
425
+ xref="paper",
426
+ yref="paper",
427
+ x = 0.05, #dfdata['Exit Date'].astype('int64').min() // 10**9,
428
+ y = .95, #dfdata['Cumulative P/L'].max(),
429
+ sizex= .9, #(dfdata['Exit Date'].astype('int64').max() - dfdata['Exit Date'].astype('int64').min()) // 10**9,
430
+ sizey= .9, #(dfdata['Cumulative P/L'].max() - dfdata['Cumulative P/L'].min()),
431
+ sizing="contain",
432
+ opacity=0.5,
433
+ layer = "below")
434
+ )
435
+
436
+ #style layout
437
+ fig.update_layout(
438
+ height = 550,
439
+ xaxis=dict(
440
+ title="Exit Date",
441
+ tickmode='array',
442
+ showgrid=False
443
+ ),
444
+ yaxis=dict(
445
+ title="Cumulative P/L",
446
+ showgrid=False
447
+ ),
448
+ legend=dict(
449
+ x=.85,
450
+ y=0.15,
451
+ traceorder="normal"
452
+ ),
453
+ plot_bgcolor = 'rgba(10, 10, 10, 1)'
454
+ )
455
+
456
+ st.plotly_chart(fig, theme=None, use_container_width=True, height=550)
457
+ st.write()
458
+ df['Per Trade Return Rate'] = df['Return Per Trade']-1
459
+
460
+ totals = pd.DataFrame([], columns = ['# of Trades', 'Wins', 'Losses', 'Win Rate', 'Profit Factor'])
461
+ data = get_hist_info(df.dropna(), principal_balance,'Per Trade Return Rate')
462
+ totals.loc[len(totals)] = list(i for i in data)
463
+
464
+ totals['Cum. P/L'] = cum_pl-principal_balance
465
+ totals['Cum. P/L (%)'] = 100*(cum_pl-principal_balance)/principal_balance
466
+
467
+ if df.empty:
468
+ st.error("Oops! None of the data provided matches your selection(s). Please try again.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
469
  else:
470
+ with st.container():
471
+ for row in totals.itertuples():
472
+ col1, col2, col3, col4= st.columns(4)
473
+ c1, c2, c3, c4 = st.columns(4)
474
+ with col1:
475
+ st.metric(
476
+ "Total Trades",
477
+ f"{row._1:.0f}",
478
+ )
479
+ with c1:
480
+ st.metric(
481
+ "Profit Factor",
482
+ f"{row._5:.2f}",
483
+ )
484
+ with col2:
485
+ st.metric(
486
+ "Wins",
487
+ f"{row.Wins:.0f}",
488
+ )
489
+ with c2:
490
+ st.metric(
491
+ "Cumulative P/L",
492
+ f"${row._6:.2f}",
493
+ f"{row._7:.2f} %",
494
+ )
495
+ with col3:
496
+ st.metric(
497
+ "Losses",
498
+ f"{row.Losses:.0f}",
499
+ )
500
+ with c3:
501
+ st.metric(
502
+ "Rolling 7 Days",
503
+ "",#f"{(1+get_rolling_stats(df,otimeheader, 30))*principal_balance:.2f}",
504
+ f"{get_rolling_stats(df,lev, otimeheader, 7):.2f}%",
505
+ )
506
+ st.metric(
507
+ "Rolling 30 Days",
508
+ "",#f"{(1+get_rolling_stats(df,otimeheader, 30))*principal_balance:.2f}",
509
+ f"{get_rolling_stats(df,lev, otimeheader, 30):.2f}%",
510
+ )
511
+
512
+ with col4:
513
+ st.metric(
514
+ "Win Rate",
515
+ f"{row._4:.1f}%",
516
+ )
517
+ with c4:
518
+ st.metric(
519
+ "Rolling 90 Days",
520
+ "",#f"{(1+get_rolling_stats(df,otimeheader, 30))*principal_balance:.2f}",
521
+ f"{get_rolling_stats(df,lev, otimeheader, 90):.2f}%",
522
+ )
523
+ st.metric(
524
+ "Rolling 180 Days",
525
+ "",#f"{(1+get_rolling_stats(df,otimeheader, 30))*principal_balance:.2f}",
526
+ f"{get_rolling_stats(df,lev, otimeheader, 180):.2f}%",
527
+ )
528
+
529
+ if bot_selections == "Pumpernickel":
530
+ grouped_df = df.groupby('Exit Date').agg({'Signal':'min','Entry Date': 'min','Exit Date': 'max','Buy Price': 'mean',
531
+ 'Sell Price' : 'max',
532
+ 'Net P/L Per Trade': 'mean',
533
+ 'Calculated Return %' : lambda x: np.round(100*lev*x.sum(),2),
534
+ 'DCA': lambda x: int(np.floor(x.max()))})
535
+ grouped_df.index = range(1, len(grouped_df)+1)
536
+ grouped_df.rename(columns={'DCA' : '# of DCAs', 'Buy Price':'Avg. Buy Price',
537
+ 'Net P/L Per Trade':'Net P/L',
538
+ 'Calculated Return %':'P/L %'}, inplace=True)
539
  else:
540
+ grouped_df = df.groupby('Exit Date').agg({'Signal':'min','Entry Date': 'min','Exit Date': 'max','Buy Price': 'mean',
541
+ 'Sell Price' : 'max',
542
+ 'Net P/L Per Trade': 'mean',
543
+ 'Calculated Return %' : lambda x: np.round(100*lev*x.sum(),2)})
544
+ grouped_df.index = range(1, len(grouped_df)+1)
545
+ grouped_df.rename(columns={'Buy Price':'Avg. Buy Price',
546
+ 'Net P/L Per Trade':'Net P/L',
547
+ 'Calculated Return %':'P/L %'}, inplace=True)
 
 
 
 
 
 
 
 
 
548
  st.subheader("Trade Logs")
549
  grouped_df['Entry Date'] = pd.to_datetime(grouped_df['Entry Date'])
550
  grouped_df['Exit Date'] = pd.to_datetime(grouped_df['Exit Date'])
551
+ st.dataframe(grouped_df.style.format({'Entry Date':'{:%m-%d-%Y %H:%M:%S}','Exit Date':'{:%m-%d-%Y %H:%M:%S}','Avg. Buy Price': '${:.4f}', 'Sell Price': '${:.4f}', 'Net P/L':'${:.2f}', 'P/L %':'{:.2f}%'})\
552
+ .applymap(my_style,subset=['Net P/L'])\
553
+ .applymap(my_style,subset=['P/L %']), use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
554
 
555
  # st.subheader("Checking Status")
556
  # if submitted:
 
558
 
559
  if __name__ == "__main__":
560
  st.set_page_config(
561
+ "Trading Bot Dashboard", layout = 'wide'
 
562
  )
563
  runapp()
564
+ # -
565
+
566
+
567
+
568
+
history.csv ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ,id,datetime,exchange,subaccount,pair,side,action,amount,price
2
+ 0,1,2024-02-12 19:05:48,,,,,,,"Webhook error:
3
+ Traceback (most recent call last):
4
+ File ""/home/doukaslewis/mysite/flask_app.py"", line 315, in webhook
5
+ dec = f_key.decrypt(enc).decode()
6
+ File ""/usr/local/lib/python3.10/site-packages/cryptography/fernet.py"", line 83, in decrypt
7
+ t"
8
+ 1,2,2024-02-12 19:10:57,,,,,,,"Webhook error:
9
+ Traceback (most recent call last):
10
+ File ""/home/doukaslewis/mysite/flask_app.py"", line 315, in webhook
11
+ f_key = bytes(f_key, ""utf-8"")
12
+ UnboundLocalError: local variable 'f_key' referenced before assignment
13
+
14
+ local variable 'f_key' referen"
15
+ 2,3,2024-02-12 19:12:50,,,,,,,"Webhook error:
16
+ Traceback (most recent call last):
17
+ File ""/home/doukaslewis/mysite/flask_app.py"", line 316, in webhook
18
+ f_key = bytes(f_key, ""utf-8"")
19
+ UnboundLocalError: local variable 'f_key' referenced before assignment
20
+
21
+ local variable 'f_key' referen"
22
+ 3,4,2024-02-12 19:17:01,,,,,,,"Webhook error:
23
+ Traceback (most recent call last):
24
+ File ""/home/doukaslewis/mysite/flask_app.py"", line 316, in webhook
25
+ dec = f_key.decrypt(enc).decode()
26
+ File ""/usr/local/lib/python3.10/site-packages/cryptography/fernet.py"", line 83, in decrypt
27
+ t"
28
+ 4,5,2024-02-12 19:18:27,,,,,,,"Webhook error:
29
+ Traceback (most recent call last):
30
+ File ""/home/doukaslewis/mysite/flask_app.py"", line 315, in webhook
31
+ dec = bytes(f_key, ""utf-8"").decrypt(enc).decode()
32
+ TypeError: encoding without a string argument
33
+
34
+ encoding without a string argument"
35
+ 5,6,2024-02-12 19:28:00,,,,,,,"Webhook error:
36
+ Traceback (most recent call last):
37
+ File ""/home/doukaslewis/mysite/flask_app.py"", line 315, in webhook
38
+ dec = f_key.decrypt(enc) #.decode()
39
+ File ""/usr/local/lib/python3.10/site-packages/cryptography/fernet.py"", line 83, in decrypt
40
+ "
41
+ 6,7,2024-02-12 19:32:09,Bybit Futures,test1,BTCUSDT,buy,open,0.001,49881.5
42
+ 7,8,2024-02-12 19:40:29,Bybit Futures,test1,,,,,No need to place order for BTCUSDT.
43
+ 8,9,2024-02-12 19:40:38,Bybit Futures,test1,BTCUSDT,sell,close,0.001,49736.9
44
+ 9,10,2024-02-12 19:40:39,Bybit Futures,test1,BTCUSDT,sell,open,0.001,49736.9
45
+ 10,11,2024-02-12 19:40:43,Bybit Futures,test1,,,,,No need to place order for BTCUSDT.
46
+ 11,12,2024-02-12 19:40:50,Bybit Futures,test1,,,,,No need to place order for BTCUSDT.
47
+ 12,13,2024-02-12 19:41:26,Bybit Futures,test1,BTCUSDT,buy,close,0.001,49733.3
48
+ 13,14,2024-02-12 19:41:31,Bybit Futures,test1,,,,,No need to place order for BTCUSDT.
49
+ 14,15,2024-02-12 19:42:16,Bybit Futures,test1,BTCUSDT,buy,open,0.003,49749.5
50
+ 15,16,2024-02-12 19:42:28,Bybit Futures,test1,BTCUSDT,buy,open,0.003,49737.1
51
+ 16,17,2024-02-12 19:42:36,Bybit Futures,test1,BTCUSDT,sell,close,0.006,49726.4
52
+ 17,18,2024-02-12 19:42:37,Bybit Futures,test1,BTCUSDT,sell,open,0.003,49738
53
+ 18,19,2024-02-12 19:42:43,Bybit Futures,test1,BTCUSDT,sell,open,0.003,49731
54
+ 19,20,2024-02-12 19:43:01,Bybit Futures,test1,BTCUSDT,buy,close,0.006,49752.4
55
+ 20,21,2024-02-12 19:51:14,Bybit Futures,test1,,,,,No need to place order for BTCUSDT.
56
+ 21,22,2024-02-12 19:51:38,Bybit Futures,test1,,,,,"Error: Get active positions:
57
+ Traceback (most recent call last):
58
+ File ""/home/doukaslewis/mysite/bybitUniFuturesClient.py"", line 236, in get_position
59
+ position = self.client.get_positions(category= ""linear"", symbol= pair)['result']['list'][0]
60
+ File ""/"
61
+ 22,23,2024-02-13 00:16:03,Bybit Futures,Pure Bread Test,ETHUSDT,sell,open,0.33,2679.29
62
+ 23,24,2024-02-13 03:11:33,Bybit Futures,Pure Bread Test,ETHUSDT,buy,close,0.33,2653.47
63
+ 24,25,2024-02-13 03:38:01,Bybit Futures,Pure Bread Test,ETHUSDT,buy,open,0.34,2642.86
64
+ 25,26,2024-02-13 09:01:02,Bybit Futures,Pure Bread Test,ETHUSDT,sell,close,0.34,2662.12
65
+ 26,27,2024-02-13 09:01:03,Bybit Futures,Pure Bread Test,ETHUSDT,sell,open,0.34,2662.12
66
+ 27,28,2024-02-13 10:26:43,Bybit Futures,Pumpernickel Test,,,,,Order size (0.0) is less than minimum size (0.1) for ATOMUSDT.
67
+ 28,29,2024-02-13 11:56:56,Bybit Futures,test1,,,,,No need to place order for BTCUSDT.
68
+ 29,30,2024-02-13 11:57:12,Bybit Futures,test1,BTCUSDT,buy,open,0.001,50013.9
69
+ 30,31,2024-02-13 11:57:16,Bybit Futures,test1,,,,,No need to place order for BTCUSDT.
70
+ 31,32,2024-02-13 11:57:39,Bybit Futures,test1,BTCUSDT,sell,close,0.001,50016.1
71
+ 32,33,2024-02-13 11:57:40,Bybit Futures,test1,BTCUSDT,sell,open,0.001,50016.1
72
+ 33,34,2024-02-13 11:58:13,Bybit Futures,test1,BTCUSDT,buy,close,0.001,49970.9
73
+ 34,35,2024-02-13 11:58:22,Bybit Futures,test1,,,,,No need to place order for BTCUSDT.
74
+ 35,36,2024-02-13 11:58:40,Bybit Futures,test1,BTCUSDT,buy,open,0.009,49985.9
75
+ 36,37,2024-02-13 11:58:50,Bybit Futures,test1,BTCUSDT,buy,open,0.009,49982.1
76
+ 37,38,2024-02-13 11:59:10,Bybit Futures,test1,BTCUSDT,sell,close,0.018,49974.5
77
+ 38,39,2024-02-13 11:59:58,Bybit Futures,test1,,,,,"Error: Get active positions:
78
+ Traceback (most recent call last):
79
+ File ""/home/doukaslewis/mysite/bybitUniFuturesClient.py"", line 236, in get_position
80
+ position = self.client.get_positions(category= ""linear"", symbol= pair)['result']['list'][0]
81
+ File ""/"
82
+ 39,40,2024-02-13 12:45:32,Bybit Futures,Pumpernickel Test,,,,,Order size (0.0) is less than minimum size (0.1) for ATOMUSDT.
83
+ 40,41,2024-02-13 13:37:01,Bybit Futures,Pure Bread Test,,,,,No need to place order for ETHUSDT.
84
+ 41,42,2024-02-13 15:15:05,Bybit Futures,Pumpernickel Test,ATOMUSDT,buy,open,19.9,9.983
85
+ 42,43,2024-02-13 15:28:34,Bybit Futures,Pumpernickel Test,ATOMUSDT,sell,close,19.9,10.095
86
+ 43,44,2024-02-13 15:28:35,Bybit Futures,Pumpernickel Test,ATOMUSDT,sell,open,19.8,10.097
87
+ 44,45,2024-02-13 16:41:32,Bybit Futures,Pumpernickel Test,ATOMUSDT,buy,close,19.8,9.974
88
+ 45,46,2024-02-13 16:41:33,Bybit Futures,Pumpernickel Test,ATOMUSDT,buy,open,20.2,9.97471782
89
+ 46,47,2024-02-13 16:58:37,Bybit Futures,Pumpernickel Test,ATOMUSDT,buy,open,20.2,9.93472772
90
+ 47,48,2024-02-13 19:08:03,Bybit Futures,Pumpernickel Test,ATOMUSDT,sell,close,40.4,10.104
91
+ 48,49,2024-02-13 19:08:04,Bybit Futures,Pumpernickel Test,ATOMUSDT,sell,open,20.3,10.104
92
+ 49,50,2024-02-13 19:29:04,Bybit Futures,Pumpernickel Test,ATOMUSDT,sell,open,20.1,10.186
93
+ 50,51,2024-02-13 19:58:20,Bybit Futures,test1,,,,,"Error: Get active positions:
94
+ Traceback (most recent call last):
95
+ File ""/home/doukaslewis/mysite/bybitUniFuturesClient.py"", line 236, in get_position
96
+ position = self.client.get_positions(category= ""linear"", symbol= pair)['result']['list'][0]
97
+ File ""/"
98
+ 51,52,2024-02-13 20:01:33,Bybit Futures,test1,BTCUSDT,buy,open,0.001,49052.6
99
+ 52,53,2024-02-13 14:02:46,Bybit Futures,test1,BTCUSDT,buy,open,0.001,49089.5
100
+ 53,54,2024-02-13 20:10:03,Bybit Futures,Pumpernickel Test,,,,,"Error: Get active positions:
101
+ Traceback (most recent call last):
102
+ File ""/home/doukaslewis/mysite/bybitUniFuturesClient.py"", line 157, in get_position
103
+ position = self.client.get_positions(category= ""linear"", symbol= pair)['result']['list'][0]
104
+ File ""/"
old_app.py ADDED
@@ -0,0 +1,731 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ---
2
+ # jupyter:
3
+ # jupytext:
4
+ # text_representation:
5
+ # extension: .py
6
+ # format_name: light
7
+ # format_version: '1.5'
8
+ # jupytext_version: 1.14.2
9
+ # kernelspec:
10
+ # display_name: Python [conda env:bbytes] *
11
+ # language: python
12
+ # name: conda-env-bbytes-py
13
+ # ---
14
+
15
+ # +
16
+ import csv
17
+ import pandas as pd
18
+ from datetime import datetime, timedelta
19
+ import numpy as np
20
+ import datetime as dt
21
+ import matplotlib.pyplot as plt
22
+ from pathlib import Path
23
+ import time
24
+ import plotly.graph_objects as go
25
+ import plotly.io as pio
26
+ from PIL import Image
27
+
28
+ import streamlit as st
29
+ import plotly.express as px
30
+ import altair as alt
31
+ import dateutil.parser
32
+ from matplotlib.colors import LinearSegmentedColormap
33
+
34
+
35
+ # +
36
+ class color:
37
+ PURPLE = '\033[95m'
38
+ CYAN = '\033[96m'
39
+ DARKCYAN = '\033[36m'
40
+ BLUE = '\033[94m'
41
+ GREEN = '\033[92m'
42
+ YELLOW = '\033[93m'
43
+ RED = '\033[91m'
44
+ BOLD = '\033[1m'
45
+ UNDERLINE = '\033[4m'
46
+ END = '\033[0m'
47
+
48
+ @st.experimental_memo
49
+ def print_PL(amnt, thresh, extras = "" ):
50
+ if amnt > 0:
51
+ return color.BOLD + color.GREEN + str(amnt) + extras + color.END
52
+ elif amnt < 0:
53
+ return color.BOLD + color.RED + str(amnt)+ extras + color.END
54
+ elif np.isnan(amnt):
55
+ return str(np.nan)
56
+ else:
57
+ return str(amnt + extras)
58
+
59
+ @st.experimental_memo
60
+ def get_headers(logtype):
61
+ otimeheader = ""
62
+ cheader = ""
63
+ plheader = ""
64
+ fmat = '%Y-%m-%d %H:%M:%S'
65
+
66
+ if logtype == "ByBit":
67
+ otimeheader = 'Create Time'
68
+ cheader = 'Contracts'
69
+ plheader = 'Closed P&L'
70
+ fmat = '%Y-%m-%d %H:%M:%S'
71
+
72
+ if logtype == "BitGet":
73
+ otimeheader = 'Date'
74
+ cheader = 'Futures'
75
+ plheader = 'Realized P/L'
76
+ fmat = '%Y-%m-%d %H:%M:%S'
77
+
78
+ if logtype == "MEXC":
79
+ otimeheader = 'Trade time'
80
+ cheader = 'Futures'
81
+ plheader = 'closing position'
82
+ fmat = '%Y/%m/%d %H:%M'
83
+
84
+ if logtype == "Binance":
85
+ otimeheader = 'Date'
86
+ cheader = 'Symbol'
87
+ plheader = 'Realized Profit'
88
+ fmat = '%Y-%m-%d %H:%M:%S'
89
+
90
+ #if logtype == "Kucoin":
91
+ # otimeheader = 'Time'
92
+ # cheader = 'Contract'
93
+ # plheader = ''
94
+ # fmat = '%Y/%m/%d %H:%M:%S'
95
+
96
+
97
+ if logtype == "Kraken":
98
+ otimeheader = 'time'
99
+ cheader = 'asset'
100
+ plheader = 'amount'
101
+ fmat = '%Y-%m-%d %H:%M:%S.%f'
102
+
103
+ if logtype == "OkX":
104
+ otimeheader = '\ufeffOrder Time'
105
+ cheader = '\ufeffInstrument'
106
+ plheader = '\ufeffPL'
107
+ fmat = '%Y-%m-%d %H:%M:%S'
108
+
109
+ return otimeheader.lower(), cheader.lower(), plheader.lower(), fmat
110
+
111
+ @st.experimental_memo
112
+ def get_coin_info(df_coin, principal_balance,plheader):
113
+ numtrades = int(len(df_coin))
114
+ numwin = int(sum(df_coin[plheader] > 0))
115
+ numloss = int(sum(df_coin[plheader] < 0))
116
+ winrate = np.round(100*numwin/numtrades,2)
117
+
118
+ grosswin = sum(df_coin[df_coin[plheader] > 0][plheader])
119
+ grossloss = sum(df_coin[df_coin[plheader] < 0][plheader])
120
+ if grossloss != 0:
121
+ pfactor = -1*np.round(grosswin/grossloss,2)
122
+ else:
123
+ pfactor = np.nan
124
+
125
+ cum_PL = np.round(sum(df_coin[plheader].values),2)
126
+ cum_PL_perc = np.round(100*cum_PL/principal_balance,2)
127
+ mean_PL = np.round(sum(df_coin[plheader].values/len(df_coin)),2)
128
+ mean_PL_perc = np.round(100*mean_PL/principal_balance,2)
129
+
130
+ return numtrades, numwin, numloss, winrate, pfactor, cum_PL, cum_PL_perc, mean_PL, mean_PL_perc
131
+
132
+ @st.experimental_memo
133
+ def get_hist_info(df_coin, principal_balance,plheader):
134
+ numtrades = int(len(df_coin))
135
+ numwin = int(sum(df_coin[plheader] > 0))
136
+ numloss = int(sum(df_coin[plheader] < 0))
137
+ if numtrades != 0:
138
+ winrate = int(np.round(100*numwin/numtrades,2))
139
+ else:
140
+ winrate = np.nan
141
+
142
+ grosswin = sum(df_coin[df_coin[plheader] > 0][plheader])
143
+ grossloss = sum(df_coin[df_coin[plheader] < 0][plheader])
144
+ if grossloss != 0:
145
+ pfactor = -1*np.round(grosswin/grossloss,2)
146
+ else:
147
+ pfactor = np.nan
148
+ return numtrades, numwin, numloss, winrate, pfactor
149
+
150
+ @st.experimental_memo
151
+ def get_rolling_stats(df, lev, otimeheader, days):
152
+ max_roll = (df[otimeheader].max() - df[otimeheader].min()).days
153
+
154
+ if max_roll >= days:
155
+ rollend = df[otimeheader].max()-timedelta(days=days)
156
+ rolling_df = df[df[otimeheader] >= rollend]
157
+
158
+ if len(rolling_df) > 0:
159
+ rolling_perc = rolling_df['Return Per Trade'].dropna().cumprod().values[-1]-1
160
+ else:
161
+ rolling_perc = np.nan
162
+ else:
163
+ rolling_perc = np.nan
164
+ return 100*rolling_perc
165
+ @st.experimental_memo
166
+ def cc_coding(row):
167
+ return ['background-color: lightgrey'] * len(row) if row['Exit Date'] <= datetime.strptime('2022-12-16 00:00:00','%Y-%m-%d %H:%M:%S').date() else [''] * len(row)
168
+ def ctt_coding(row):
169
+ return ['background-color: lightgrey'] * len(row) if row['Exit Date'] <= datetime.strptime('2023-01-02 00:00:00','%Y-%m-%d %H:%M:%S').date() else [''] * len(row)
170
+ def conditional_formatter(value):
171
+ return "${:.2f}".format(value) if not (abs(value) < 1.00) else "${:.4f}".format(value)
172
+ @st.experimental_memo
173
+ def my_style(v, props=''):
174
+ props = 'color:red' if v < 0 else 'color:green'
175
+ return props
176
+
177
+ def filt_df(df, cheader, symbol_selections):
178
+
179
+ df = df.copy()
180
+ df = df[df[cheader].isin(symbol_selections)]
181
+
182
+ return df
183
+
184
+ def tv_reformat(close50filename):
185
+ try:
186
+ data = pd.read_csv(open(close50filename,'r'), sep='[,|\t]', engine='python')
187
+ except:
188
+ data = pd.DataFrame([])
189
+
190
+ if data.empty:
191
+ return data
192
+ else:
193
+ st.dataframe(data)
194
+ entry_df = data[data['Type'].str.contains("Entry")]
195
+ exit_df = data[data['Type'].str.contains("Exit")]
196
+
197
+ entry_df.index = range(len(entry_df))
198
+ exit_df.index = range(len(exit_df))
199
+
200
+ df = pd.DataFrame([], columns=['Trade','Entry Date','Buy Price', 'Sell Price','Exit Date', 'P/L per token', 'P/L %', 'Drawdown %'])
201
+
202
+ df['Signal'] = [string.split(' ')[1] for string in entry_df['Type']]
203
+ df['Trade'] = entry_df.index
204
+ df['Entry Date'] = entry_df['Date/Time']
205
+ df['Buy Price'] = entry_df['Price USDT']
206
+
207
+ df['Sell Price'] = exit_df['Price USDT']
208
+ df['Exit Date'] = exit_df['Date/Time']
209
+ df['P/L per token'] = df['Sell Price'] - df['Buy Price']
210
+ df['P/L %'] = exit_df['Profit %']
211
+ df['Drawdown %'] = exit_df['Drawdown %']
212
+ df['Close 50'] = [int(i == "Close 50% of Position") for i in exit_df['Signal']]
213
+ df = df.sort_values(['Entry Date','Close 50'], ascending = [False, True])
214
+ df.index = range(len(df))
215
+
216
+ df.loc[df['Close 50'] == 1, 'Exit Date'] = np.copy(df.loc[df[df['Close 50'] == 1].index.values -1]['Exit Date'])
217
+
218
+ grouped_df = df.groupby('Entry Date').agg({'Signal' : 'first', 'Entry Date': 'min', 'Buy Price':'mean',
219
+ 'Sell Price' : 'mean',
220
+ 'Exit Date': 'max',
221
+ 'P/L per token': 'mean',
222
+ 'P/L %' : 'mean'})
223
+
224
+ grouped_df.insert(0,'Trade', range(len(grouped_df)))
225
+ grouped_df.index = range(len(grouped_df))
226
+ return grouped_df
227
+
228
+ def load_data(filename, otimeheader, fmat):
229
+ df = pd.read_csv(open(filename,'r'), sep='\t') # so as not to mutate cached value
230
+ close50filename = filename.split('.')[0] + '-50.' + filename.split('.')[1]
231
+ df2 = tv_reformat(close50filename)
232
+
233
+ if filename == "CT-Trade-Log.csv":
234
+ df.columns = ['Trade','Entry Date','Buy Price', 'Sell Price','Exit Date', 'P/L per token', 'P/L %', 'Drawdown %']
235
+ df.insert(1, 'Signal', ['Long']*len(df))
236
+ elif filename == "CC-Trade-Log.csv" or "PB-Trade-Log.csv":
237
+ df.columns = ['Trade','Signal','Entry Date','Buy Price', 'Sell Price','Exit Date', 'P/L per token', 'P/L %', 'Drawdown %']
238
+ else:
239
+ df.columns = ['Trade','Signal','Entry Date','Buy Price', 'Sell Price','Exit Date', 'P/L per token', 'P/L %']
240
+
241
+ if filename != "CT-Toasted-Trade-Log.csv":
242
+ df['Signal'] = df['Signal'].str.replace(' ', '', regex=True)
243
+ df['Buy Price'] = df['Buy Price'].str.replace('$', '', regex=True)
244
+ df['Sell Price'] = df['Sell Price'].str.replace('$', '', regex=True)
245
+ df['Buy Price'] = df['Buy Price'].str.replace(',', '', regex=True)
246
+ df['Sell Price'] = df['Sell Price'].str.replace(',', '', regex=True)
247
+ df['P/L per token'] = df['P/L per token'].str.replace('$', '', regex=True)
248
+ df['P/L per token'] = df['P/L per token'].str.replace(',', '', regex=True)
249
+ df['P/L %'] = df['P/L %'].str.replace('%', '', regex=True)
250
+
251
+ df['Buy Price'] = pd.to_numeric(df['Buy Price'])
252
+ df['Sell Price'] = pd.to_numeric(df['Sell Price'])
253
+ df['P/L per token'] = pd.to_numeric(df['P/L per token'])
254
+ df['P/L %'] = pd.to_numeric(df['P/L %'])
255
+
256
+ if df2.empty:
257
+ df = df
258
+ else:
259
+ df = pd.concat([df,df2], axis=0, ignore_index=True)
260
+
261
+ if filename == "CT-Trade-Log.csv":
262
+ df['Signal'] = ['Long']*len(df)
263
+
264
+ dateheader = 'Date'
265
+ theader = 'Time'
266
+
267
+ df[dateheader] = [tradetimes.split(" ")[0] for tradetimes in df[otimeheader].values]
268
+ df[theader] = [tradetimes.split(" ")[1] for tradetimes in df[otimeheader].values]
269
+
270
+ df[otimeheader]= [dateutil.parser.parse(date+' '+time)
271
+ for date,time in zip(df[dateheader],df[theader])]
272
+ df[otimeheader] = pd.to_datetime(df[otimeheader])
273
+ df['Exit Date'] = pd.to_datetime(df['Exit Date'])
274
+ df.sort_values(by=otimeheader, inplace=True)
275
+
276
+ df[dateheader] = [dateutil.parser.parse(date).date() for date in df[dateheader]]
277
+ df[theader] = [dateutil.parser.parse(time).time() for time in df[theader]]
278
+ df['Trade'] = df.index + 1 #reindex
279
+
280
+ if filename == "CT-Trade-Log.csv":
281
+ df['DCA'] = np.nan
282
+
283
+ for exit in pd.unique(df['Exit Date']):
284
+ df_exit = df[df['Exit Date']==exit]
285
+ if dateutil.parser.parse(str(exit)) < dateutil.parser.parse('2023-02-07 13:00:00'):
286
+ for i in range(len(df_exit)):
287
+ ind = df_exit.index[i]
288
+ df.loc[ind,'DCA'] = i+1
289
+
290
+ else:
291
+ for i in range(len(df_exit)):
292
+ ind = df_exit.index[i]
293
+ df.loc[ind,'DCA'] = i+1.1
294
+ return df
295
+
296
+
297
+ def get_sd_df(sd_df, sd, bot_selections, dca1, dca2, dca3, dca4, dca5, dca6, fees, lev, dollar_cap, principal_balance):
298
+ sd = 2*.00026
299
+ # ------ Standard Dev. Calculations.
300
+ if bot_selections == "Cinnamon Toast":
301
+ dca_map = {1: dca1/100, 2: dca2/100, 3: dca3/100, 4: dca4/100, 1.1: dca5/100, 2.1: dca6/100}
302
+ sd_df['DCA %'] = sd_df['DCA'].map(dca_map)
303
+ sd_df['Calculated Return % (+)'] = df['Signal'].map(signal_map)*(df['DCA %'])*(1-fees)*((df['Sell Price']*(1+df['Signal'].map(signal_map)*sd) - df['Buy Price']*(1-df['Signal'].map(signal_map)*sd))/df['Buy Price']*(1-df['Signal'].map(signal_map)*sd) - fees) #accounts for fees on open and close of trade
304
+ sd_df['Calculated Return % (-)'] = df['Signal'].map(signal_map)*(df['DCA %'])*(1-fees)*((df['Sell Price']*(1-df['Signal'].map(signal_map)*sd)-df['Buy Price']*(1+df['Signal'].map(signal_map)*sd))/df['Buy Price']*(1+df['Signal'].map(signal_map)*sd) - fees) #accounts for fees on open and close of trade
305
+ sd_df['DCA'] = np.floor(sd_df['DCA'].values)
306
+
307
+ sd_df['Return Per Trade (+)'] = np.nan
308
+ sd_df['Return Per Trade (-)'] = np.nan
309
+ sd_df['Balance used in Trade (+)'] = np.nan
310
+ sd_df['Balance used in Trade (-)'] = np.nan
311
+ sd_df['New Balance (+)'] = np.nan
312
+ sd_df['New Balance (-)'] = np.nan
313
+
314
+ g1 = sd_df.groupby('Exit Date').sum(numeric_only=True)['Calculated Return % (+)'].reset_index(name='Return Per Trade (+)')
315
+ g2 = sd_df.groupby('Exit Date').sum(numeric_only=True)['Calculated Return % (-)'].reset_index(name='Return Per Trade (-)')
316
+ sd_df.loc[sd_df['DCA']==1.0,'Return Per Trade (+)'] = 1+lev*g1['Return Per Trade (+)'].values
317
+ sd_df.loc[sd_df['DCA']==1.0,'Return Per Trade (-)'] = 1+lev*g2['Return Per Trade (-)'].values
318
+
319
+ sd_df['Compounded Return (+)'] = sd_df['Return Per Trade (+)'].cumprod()
320
+ sd_df['Compounded Return (-)'] = sd_df['Return Per Trade (-)'].cumprod()
321
+ sd_df.loc[sd_df['DCA']==1.0,'New Balance (+)'] = [min(dollar_cap/lev, bal*principal_balance) for bal in sd_df.loc[sd_df['DCA']==1.0,'Compounded Return (+)']]
322
+ sd_df.loc[sd_df['DCA']==1.0,'Balance used in Trade (+)'] = np.concatenate([[principal_balance], sd_df.loc[sd_df['DCA']==1.0,'New Balance (+)'].values[:-1]])
323
+
324
+ sd_df.loc[sd_df['DCA']==1.0,'New Balance (-)'] = [min(dollar_cap/lev, bal*principal_balance) for bal in sd_df.loc[sd_df['DCA']==1.0,'Compounded Return (-)']]
325
+ sd_df.loc[sd_df['DCA']==1.0,'Balance used in Trade (-)'] = np.concatenate([[principal_balance], sd_df.loc[sd_df['DCA']==1.0,'New Balance (-)'].values[:-1]])
326
+ else:
327
+ sd_df['Calculated Return % (+)'] = df['Signal'].map(signal_map)*(1-fees)*((df['Sell Price']*(1+df['Signal'].map(signal_map)*sd) - df['Buy Price']*(1-df['Signal'].map(signal_map)*sd))/df['Buy Price']*(1-df['Signal'].map(signal_map)*sd) - fees) #accounts for fees on open and close of trade
328
+ sd_df['Calculated Return % (-)'] = df['Signal'].map(signal_map)*(1-fees)*((df['Sell Price']*(1-df['Signal'].map(signal_map)*sd)-df['Buy Price']*(1+df['Signal'].map(signal_map)*sd))/df['Buy Price']*(1+df['Signal'].map(signal_map)*sd) - fees) #accounts for fees on open and close of trade
329
+ sd_df['Return Per Trade (+)'] = np.nan
330
+ sd_df['Return Per Trade (-)'] = np.nan
331
+
332
+ g1 = sd_df.groupby('Exit Date').sum(numeric_only=True)['Calculated Return % (+)'].reset_index(name='Return Per Trade (+)')
333
+ g2 = sd_df.groupby('Exit Date').sum(numeric_only=True)['Calculated Return % (-)'].reset_index(name='Return Per Trade (-)')
334
+ sd_df['Return Per Trade (+)'] = 1+lev*g1['Return Per Trade (+)'].values
335
+ sd_df['Return Per Trade (-)'] = 1+lev*g2['Return Per Trade (-)'].values
336
+
337
+ sd_df['Compounded Return (+)'] = sd_df['Return Per Trade (+)'].cumprod()
338
+ sd_df['Compounded Return (-)'] = sd_df['Return Per Trade (-)'].cumprod()
339
+ sd_df['New Balance (+)'] = [min(dollar_cap/lev, bal*principal_balance) for bal in sd_df['Compounded Return (+)']]
340
+ sd_df['Balance used in Trade (+)'] = np.concatenate([[principal_balance], sd_df['New Balance (+)'].values[:-1]])
341
+
342
+ sd_df['New Balance (-)'] = [min(dollar_cap/lev, bal*principal_balance) for bal in sd_df['Compounded Return (-)']]
343
+ sd_df['Balance used in Trade (-)'] = np.concatenate([[principal_balance], sd_df['New Balance (-)'].values[:-1]])
344
+
345
+ sd_df['Net P/L Per Trade (+)'] = (sd_df['Return Per Trade (+)']-1)*sd_df['Balance used in Trade (+)']
346
+ sd_df['Cumulative P/L (+)'] = sd_df['Net P/L Per Trade (+)'].cumsum()
347
+
348
+ sd_df['Net P/L Per Trade (-)'] = (sd_df['Return Per Trade (-)']-1)*sd_df['Balance used in Trade (-)']
349
+ sd_df['Cumulative P/L (-)'] = sd_df['Net P/L Per Trade (-)'].cumsum()
350
+ return sd_df
351
+
352
+ def runapp() -> None:
353
+ bot_selections = "Pure Bread"
354
+ otimeheader = 'Exit Date'
355
+ fmat = '%Y-%m-%d %H:%M:%S'
356
+ fees = .075/100
357
+
358
+ st.header(f"{bot_selections} Performance Dashboard :bread: :moneybag:")
359
+ no_errors = True
360
+ st.write("Welcome to the Trading Bot Dashboard by BreadBytes! You can use this dashboard to track " +
361
+ "the performance of our trading bots.")
362
+
363
+ if bot_selections == "Cinnamon Toast":
364
+ lev_cap = 5
365
+ dollar_cap = 1000000000.00
366
+ data = load_data("CT-Trade-Log.csv",otimeheader, fmat)
367
+ if bot_selections == "French Toast":
368
+ lev_cap = 3
369
+ dollar_cap = 10000000000.00
370
+ data = load_data("FT-Trade-Log.csv",otimeheader, fmat)
371
+ if bot_selections == "Short Bread":
372
+ lev_cap = 5
373
+ dollar_cap = 1000000000.00
374
+ data = load_data("SB-Trade-Log.csv",otimeheader, fmat)
375
+ if bot_selections == "Cosmic Cupcake":
376
+ lev_cap = 3
377
+ dollar_cap = 1000000000.00
378
+ data = load_data("CC-Trade-Log.csv",otimeheader, fmat)
379
+ if bot_selections == "Pure Bread":
380
+ lev_cap = 3
381
+ dollar_cap = 1000000000.00
382
+ data = load_data("PB-Trade-Log.csv",otimeheader, fmat)
383
+
384
+ df = data.copy(deep=True)
385
+
386
+ dateheader = 'Date'
387
+ theader = 'Time'
388
+
389
+ st.subheader("Choose your settings:")
390
+ with st.form("user input", ):
391
+ if no_errors:
392
+ with st.container():
393
+ col1, col2 = st.columns(2)
394
+ with col1:
395
+ try:
396
+ startdate = st.date_input("Start Date", value=pd.to_datetime(df[otimeheader]).min())
397
+ except:
398
+ st.error("Please select your exchange or upload a supported trade log file.")
399
+ no_errors = False
400
+ with col2:
401
+ try:
402
+ enddate = st.date_input("End Date", value=datetime.today())
403
+ except:
404
+ st.error("Please select your exchange or upload a supported trade log file.")
405
+ no_errors = False
406
+ #st.sidebar.subheader("Customize your Dashboard")
407
+
408
+ if no_errors and (enddate < startdate):
409
+ st.error("End Date must be later than Start date. Please try again.")
410
+ no_errors = False
411
+ with st.container():
412
+ col1,col2 = st.columns(2)
413
+ with col2:
414
+ lev = st.number_input('Leverage', min_value=1, value=1, max_value= lev_cap, step=1)
415
+ with col1:
416
+ principal_balance = st.number_input('Starting Balance', min_value=0.00, value=1000.00, max_value= dollar_cap, step=.01)
417
+
418
+ if bot_selections == "Cinnamon Toast":
419
+ st.write("Choose your DCA setup (for trades before 02/07/2023)")
420
+ with st.container():
421
+ col1, col2, col3, col4 = st.columns(4)
422
+ with col1:
423
+ dca1 = st.number_input('DCA 1 Allocation', min_value=0, value=25, max_value= 100, step=1)
424
+ with col2:
425
+ dca2 = st.number_input('DCA 2 Allocation', min_value=0, value=25, max_value= 100, step=1)
426
+ with col3:
427
+ dca3 = st.number_input('DCA 3 Allocation', min_value=0, value=25, max_value= 100, step=1)
428
+ with col4:
429
+ dca4 = st.number_input('DCA 4 Allocation', min_value=0, value=25, max_value= 100, step=1)
430
+ st.write("Choose your DCA setup (for trades on or after 02/07/2023)")
431
+ with st.container():
432
+ col1, col2 = st.columns(2)
433
+ with col1:
434
+ dca5 = st.number_input('DCA 1 Allocation', min_value=0, value=50, max_value= 100, step=1)
435
+ with col2:
436
+ dca6 = st.number_input('DCA 2 Allocation', min_value=0, value=50, max_value= 100, step=1)
437
+
438
+ #hack way to get button centered
439
+ c = st.columns(9)
440
+ with c[4]:
441
+ submitted = st.form_submit_button("Get Cookin'!")
442
+
443
+ if submitted and principal_balance * lev > dollar_cap:
444
+ lev = np.floor(dollar_cap/principal_balance)
445
+ st.error(f"WARNING: (Starting Balance)*(Leverage) exceeds the ${dollar_cap} limit. Using maximum available leverage of {lev}")
446
+
447
+ if submitted and no_errors:
448
+ df = df[(df[dateheader] >= startdate) & (df[dateheader] <= enddate)]
449
+ signal_map = {'Long': 1, 'Short':-1}
450
+
451
+
452
+ if len(df) == 0:
453
+ st.error("There are no available trades matching your selections. Please try again!")
454
+ no_errors = False
455
+
456
+ if no_errors:
457
+ if bot_selections == "Cinnamon Toast":
458
+ dca_map = {1: dca1/100, 2: dca2/100, 3: dca3/100, 4: dca4/100, 1.1: dca5/100, 2.1: dca6/100}
459
+ df['DCA %'] = df['DCA'].map(dca_map)
460
+ df['Calculated Return %'] = df['Signal'].map(signal_map)*(df['DCA %'])*(1-fees)*((df['Sell Price']-df['Buy Price'])/df['Buy Price'] - fees) #accounts for fees on open and close of trade
461
+ df['DCA'] = np.floor(df['DCA'].values)
462
+
463
+ df['Return Per Trade'] = np.nan
464
+ df['Balance used in Trade'] = np.nan
465
+ df['New Balance'] = np.nan
466
+
467
+ g = df.groupby('Exit Date').sum(numeric_only=True)['Calculated Return %'].reset_index(name='Return Per Trade')
468
+ df.loc[df['DCA']==1.0,'Return Per Trade'] = 1+lev*g['Return Per Trade'].values
469
+
470
+ df['Compounded Return'] = df['Return Per Trade'].cumprod()
471
+ df.loc[df['DCA']==1.0,'New Balance'] = [min(dollar_cap/lev, bal*principal_balance) for bal in df.loc[df['DCA']==1.0,'Compounded Return']]
472
+ df.loc[df['DCA']==1.0,'Balance used in Trade'] = np.concatenate([[principal_balance], df.loc[df['DCA']==1.0,'New Balance'].values[:-1]])
473
+ else:
474
+ df['Calculated Return %'] = df['Signal'].map(signal_map)*(1-fees)*((df['Sell Price']-df['Buy Price'])/df['Buy Price'] - fees) #accounts for fees on open and close of trade
475
+ df['Return Per Trade'] = np.nan
476
+ g = df.groupby('Exit Date').sum(numeric_only=True)['Calculated Return %'].reset_index(name='Return Per Trade')
477
+ df['Return Per Trade'] = 1+lev*g['Return Per Trade'].values
478
+
479
+ df['Compounded Return'] = df['Return Per Trade'].cumprod()
480
+ df['New Balance'] = [min(dollar_cap/lev, bal*principal_balance) for bal in df['Compounded Return']]
481
+ df['Balance used in Trade'] = np.concatenate([[principal_balance], df['New Balance'].values[:-1]])
482
+ df['Net P/L Per Trade'] = (df['Return Per Trade']-1)*df['Balance used in Trade']
483
+ df['Cumulative P/L'] = df['Net P/L Per Trade'].cumsum()
484
+
485
+ if bot_selections == "Cinnamon Toast" or bot_selections == "Cosmic Cupcake" or bot_selections == "Pure Bread":
486
+ cum_pl = df.loc[df.drop('Drawdown %', axis=1).dropna().index[-1],'Cumulative P/L'] + principal_balance
487
+ #cum_sdp = sd_df.loc[sd_df.drop('Drawdown %', axis=1).dropna().index[-1],'Cumulative P/L (+)'] + principal_balance
488
+ #cum_sdm = sd_df.loc[sd_df.drop('Drawdown %', axis=1).dropna().index[-1],'Cumulative P/L (-)'] + principal_balance
489
+ else:
490
+ cum_pl = df.loc[df.dropna().index[-1],'Cumulative P/L'] + principal_balance
491
+ #cum_sdp = sd_df.loc[sd_df.dropna().index[-1],'Cumulative P/L (+)'] + principal_balance
492
+ #cum_sdm = sd_df.loc[sd_df.dropna().index[-1],'Cumulative P/L (-)'] + principal_balance
493
+ #sd = 2*.00026
494
+ #sd_df = get_sd_df(get_sd_df(df.copy(), sd, bot_selections, dca1, dca2, dca3, dca4, dca5, dca6, fees, lev, dollar_cap, principal_balance)
495
+
496
+ effective_return = 100*((cum_pl - principal_balance)/principal_balance)
497
+
498
+ st.header(f"{bot_selections} Results")
499
+ with st.container():
500
+
501
+ if len(bot_selections) > 1:
502
+ col1, col2 = st.columns(2)
503
+ with col1:
504
+ st.metric(
505
+ "Total Account Balance",
506
+ f"${cum_pl:.2f}",
507
+ f"{100*(cum_pl-principal_balance)/(principal_balance):.2f} %",
508
+ )
509
+
510
+ # with col2:
511
+ # st.write("95% of trades should fall within this 2 std. dev. range.")
512
+ # st.metric(
513
+ # "High Range (+ 2 std. dev.)",
514
+ # f"", #${cum_sdp:.2f}
515
+ # f"{100*(cum_sdp-principal_balance)/(principal_balance):.2f} %",
516
+ # )
517
+ # st.metric(
518
+ # "Low Range (- 2 std. dev.)",
519
+ # f"" ,#${cum_sdm:.2f}"
520
+ # f"{100*(cum_sdm-principal_balance)/(principal_balance):.2f} %",
521
+ # )
522
+ if bot_selections == "Cinnamon Toast" or bot_selections == "Cosmic Cupcake" or bot_selections == "Pure Bread":
523
+ #st.line_chart(data=df.drop('Drawdown %', axis=1).dropna(), x='Exit Date', y='Cumulative P/L', use_container_width=True)
524
+ dfdata = df.drop('Drawdown %', axis=1).dropna()
525
+ #sd_df = sd_df.drop('Drawdown %', axis=1).dropna()
526
+ else:
527
+ #st.line_chart(data=df.dropna(), x='Exit Date', y='Cumulative P/L', use_container_width=True)
528
+ dfdata = df.dropna()
529
+ #sd_df = sd_df.dropna()
530
+
531
+ # Create figure
532
+ fig = go.Figure()
533
+
534
+ pyLogo = Image.open("logo.png")
535
+
536
+ # fig.add_traces(go.Scatter(x=sd_df['Exit Date'], y = sd_df['Cumulative P/L (+)'],line_shape='spline',
537
+ # line = dict(smoothing = 1.3, color='rgba(31, 119, 200,0)'), showlegend = False)
538
+ # )
539
+
540
+ # fig.add_traces(go.Scatter(x=sd_df['Exit Date'], y = sd_df['Cumulative P/L (-)'],
541
+ # line = dict(smoothing = 1.3, color='rgba(31, 119, 200,0)'), line_shape='spline',
542
+ # fill='tonexty',
543
+ # fillcolor = 'rgba(31, 119, 200,.2)', name = '+/- Standard Deviation')
544
+ # )
545
+
546
+ # Add trace
547
+ fig.add_trace(
548
+ go.Scatter(x=dfdata['Exit Date'], y=np.round(dfdata['Cumulative P/L'].values,2), line_shape='spline',
549
+ line = {'smoothing': 1.0, 'color' : 'rgba(31, 119, 200,.8)'},
550
+ name='Cumulative P/L')
551
+ )
552
+ buyhold = (principal_balance/dfdata['Buy Price'][dfdata.index[0]])*(dfdata['Buy Price']-dfdata['Buy Price'][dfdata.index[0]])
553
+ fig.add_trace(go.Scatter(x=dfdata['Exit Date'], y=np.round(buyhold.values,2), line_shape='spline',
554
+ line = {'smoothing': 1.0, 'color' :'red'}, name = 'Buy & Hold Return')
555
+ )
556
+
557
+ fig.add_layout_image(
558
+ dict(
559
+ source=pyLogo,
560
+ xref="paper",
561
+ yref="paper",
562
+ x = 0.05, #dfdata['Exit Date'].astype('int64').min() // 10**9,
563
+ y = .85, #dfdata['Cumulative P/L'].max(),
564
+ sizex= .9, #(dfdata['Exit Date'].astype('int64').max() - dfdata['Exit Date'].astype('int64').min()) // 10**9,
565
+ sizey= .9, #(dfdata['Cumulative P/L'].max() - dfdata['Cumulative P/L'].min()),
566
+ sizing="contain",
567
+ opacity=0.2,
568
+ layer = "below")
569
+ )
570
+
571
+ #style layout
572
+ fig.update_layout(
573
+ height = 600,
574
+ xaxis=dict(
575
+ title="Exit Date",
576
+ tickmode='array',
577
+ ),
578
+ yaxis=dict(
579
+ title="Cumulative P/L"
580
+ ) )
581
+
582
+ st.plotly_chart(fig, theme=None, use_container_width=True,height=600)
583
+ st.write()
584
+ df['Per Trade Return Rate'] = df['Return Per Trade']-1
585
+
586
+ totals = pd.DataFrame([], columns = ['# of Trades', 'Wins', 'Losses', 'Win Rate', 'Profit Factor'])
587
+ if bot_selections == "Cinnamon Toast" or bot_selections == "Cosmic Cupcake" or bot_selections == "Pure Bread":
588
+ data = get_hist_info(df.drop('Drawdown %', axis=1).dropna(), principal_balance,'Per Trade Return Rate')
589
+ else:
590
+ data = get_hist_info(df.dropna(), principal_balance,'Per Trade Return Rate')
591
+ totals.loc[len(totals)] = list(i for i in data)
592
+
593
+ totals['Cum. P/L'] = cum_pl-principal_balance
594
+ totals['Cum. P/L (%)'] = 100*(cum_pl-principal_balance)/principal_balance
595
+
596
+ if df.empty:
597
+ st.error("Oops! None of the data provided matches your selection(s). Please try again.")
598
+ else:
599
+ with st.container():
600
+ for row in totals.itertuples():
601
+ col1, col2, col3, col4= st.columns(4)
602
+ c1, c2, c3, c4 = st.columns(4)
603
+ with col1:
604
+ st.metric(
605
+ "Total Trades",
606
+ f"{row._1:.0f}",
607
+ )
608
+ with c1:
609
+ st.metric(
610
+ "Profit Factor",
611
+ f"{row._5:.2f}",
612
+ )
613
+ with col2:
614
+ st.metric(
615
+ "Wins",
616
+ f"{row.Wins:.0f}",
617
+ )
618
+ with c2:
619
+ st.metric(
620
+ "Cumulative P/L",
621
+ f"${row._6:.2f}",
622
+ f"{row._7:.2f} %",
623
+ )
624
+ with col3:
625
+ st.metric(
626
+ "Losses",
627
+ f"{row.Losses:.0f}",
628
+ )
629
+ with c3:
630
+ st.metric(
631
+ "Rolling 7 Days",
632
+ "",#f"{(1+get_rolling_stats(df,otimeheader, 30))*principal_balance:.2f}",
633
+ f"{get_rolling_stats(df,lev, otimeheader, 7):.2f}%",
634
+ )
635
+ st.metric(
636
+ "Rolling 30 Days",
637
+ "",#f"{(1+get_rolling_stats(df,otimeheader, 30))*principal_balance:.2f}",
638
+ f"{get_rolling_stats(df,lev, otimeheader, 30):.2f}%",
639
+ )
640
+
641
+ with col4:
642
+ st.metric(
643
+ "Win Rate",
644
+ f"{row._4:.1f}%",
645
+ )
646
+ with c4:
647
+ st.metric(
648
+ "Rolling 90 Days",
649
+ "",#f"{(1+get_rolling_stats(df,otimeheader, 30))*principal_balance:.2f}",
650
+ f"{get_rolling_stats(df,lev, otimeheader, 90):.2f}%",
651
+ )
652
+ st.metric(
653
+ "Rolling 180 Days",
654
+ "",#f"{(1+get_rolling_stats(df,otimeheader, 30))*principal_balance:.2f}",
655
+ f"{get_rolling_stats(df,lev, otimeheader, 180):.2f}%",
656
+ )
657
+
658
+ if bot_selections == "Cinnamon Toast":
659
+ if submitted:
660
+ grouped_df = df.groupby('Exit Date').agg({'Signal':'min','Entry Date': 'min','Exit Date': 'max','Buy Price': 'mean',
661
+ 'Sell Price' : 'max',
662
+ 'Net P/L Per Trade': 'mean',
663
+ 'Calculated Return %' : lambda x: np.round(100*lev*x.sum(),2),
664
+ 'DCA': lambda x: int(np.floor(x.max()))})
665
+ grouped_df.index = range(1, len(grouped_df)+1)
666
+ grouped_df.rename(columns={'DCA' : '# of DCAs', 'Buy Price':'Avg. Buy Price',
667
+ 'Net P/L Per Trade':'Net P/L',
668
+ 'Calculated Return %':'P/L %'}, inplace=True)
669
+ else:
670
+ dca_map = {1: 25/100, 2: 25/100, 3: 25/100, 4: 25/100, 1.1: 50/100, 2.1: 50/100}
671
+ df['DCA %'] = df['DCA'].map(dca_map)
672
+ df['Calculated Return %'] = (df['DCA %'])*(1-fees)*((df['Sell Price']-df['Buy Price'])/df['Buy Price'] - fees) #accounts for fees on open and close of trade
673
+
674
+ grouped_df = df.groupby('Exit Date').agg({'Signal':'min','Entry Date': 'min','Exit Date': 'max','Buy Price': 'mean',
675
+ 'Sell Price' : 'max',
676
+ 'P/L per token': 'mean',
677
+ 'Calculated Return %' : lambda x: np.round(100*x.sum(),2),
678
+ 'DCA': lambda x: int(np.floor(x.max()))})
679
+ grouped_df.index = range(1, len(grouped_df)+1)
680
+ grouped_df.rename(columns={'DCA' : '# of DCAs', 'Buy Price':'Avg. Buy Price',
681
+ 'Calculated Return %':'P/L %',
682
+ 'P/L per token':'Net P/L'}, inplace=True)
683
+
684
+ else:
685
+ if submitted:
686
+ grouped_df = df.groupby('Exit Date').agg({'Signal':'min','Entry Date': 'min','Exit Date': 'max','Buy Price': 'mean',
687
+ 'Sell Price' : 'max',
688
+ 'Net P/L Per Trade': 'mean',
689
+ 'Calculated Return %' : lambda x: np.round(100*lev*x.sum(),2)})
690
+ grouped_df.index = range(1, len(grouped_df)+1)
691
+ grouped_df.rename(columns={'Buy Price':'Avg. Buy Price',
692
+ 'Net P/L Per Trade':'Net P/L',
693
+ 'Calculated Return %':'P/L %'}, inplace=True)
694
+ else:
695
+ grouped_df = df.groupby('Exit Date').agg({'Signal':'min','Entry Date': 'min','Exit Date': 'max','Buy Price': 'mean',
696
+ 'Sell Price' : 'max',
697
+ 'P/L per token': 'mean',
698
+ 'P/L %':'mean'})
699
+ grouped_df.index = range(1, len(grouped_df)+1)
700
+ grouped_df.rename(columns={'Buy Price':'Avg. Buy Price',
701
+ 'P/L per token':'Net P/L'}, inplace=True)
702
+ st.subheader("Trade Logs")
703
+ grouped_df['Entry Date'] = pd.to_datetime(grouped_df['Entry Date'])
704
+ grouped_df['Exit Date'] = pd.to_datetime(grouped_df['Exit Date'])
705
+ if bot_selections == "Cosmic Cupcake" or bot_selections == "CT Toasted":
706
+ coding = cc_coding if bot_selections == "Cosmic Cupcake" else ctt_coding
707
+ st.dataframe(grouped_df.style.format({'Entry Date':'{:%m-%d-%Y %H:%M:%S}','Exit Date':'{:%m-%d-%Y %H:%M:%S}','Avg. Buy Price': '${:.2f}', 'Sell Price': '${:.2f}', 'Net P/L':'${:.2f}', 'P/L %':'{:.2f}%'})\
708
+ .apply(coding, axis=1)\
709
+ .applymap(my_style,subset=['Net P/L'])\
710
+ .applymap(my_style,subset=['P/L %']), use_container_width=True)
711
+ # new_title = '<div style="text-align: right;"><span style="background-color:lightgrey;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span> Not Live Traded</div>'
712
+ # st.markdown(new_title, unsafe_allow_html=True)
713
+ elif bot_selections == "Pure Bread":
714
+ st.dataframe(grouped_df.style.format({'Entry Date':'{:%m-%d-%Y %H:%M:%S}','Exit Date':'{:%m-%d-%Y %H:%M:%S}','Avg. Buy Price': '${:.4f}', 'Sell Price': '${:.4f}', 'Net P/L':conditional_formatter, 'P/L %':'{:.2f}%'})\
715
+ .applymap(my_style,subset=['Net P/L'])\
716
+ .applymap(my_style,subset=['P/L %']), use_container_width=True)
717
+ else:
718
+ st.dataframe(grouped_df.style.format({'Entry Date':'{:%m-%d-%Y %H:%M:%S}','Exit Date':'{:%m-%d-%Y %H:%M:%S}','Avg. Buy Price': '${:.2f}', 'Sell Price': '${:.2f}', 'Net P/L':'${:.2f}', 'P/L %':'{:.2f}%'})\
719
+ .applymap(my_style,subset=['Net P/L'])\
720
+ .applymap(my_style,subset=['P/L %']), use_container_width=True)
721
+
722
+ # st.subheader("Checking Status")
723
+ # if submitted:
724
+ # st.dataframe(sd_df)
725
+
726
+ if __name__ == "__main__":
727
+ st.set_page_config(
728
+ "Trading Bot Dashboard",
729
+ layout="wide",
730
+ )
731
+ runapp()