import marimo

__generated_with = "0.19.6"
app = marimo.App(width="full", auto_download=["ipynb"], sql_output="pandas")


@app.cell
def _():
    import marimo as mo
    return (mo,)


@app.cell
def _():
    import urllib
    import sqlalchemy

    host = "81.71.3.24"
    port = 6785
    username = "leopard"
    password = urllib.parse.quote_plus("9NEzFzovnddf@PyEP?e*AYAWnCyd7UhYwQK$pJf>7?ccFiN^x4$eKEZ5~E<7<+~X")
    database = "leopard_dev"
    engine = sqlalchemy.create_engine(f"postgresql://{username}:{password}@{host}:{port}/{database}")
    return (engine,)


@app.cell
def _(engine, mo):
    dailies_df = mo.sql(
        f"""
        select trade_date,
               open * factor         as Open,
               close * factor        as Close,
               high * factor         as High,
               low * factor          as Low,
               volume                as Volume,
               coalesce(factor, 1.0) as factor
        from leopard_daily daily
                 left join leopard_stock stock on stock.id = daily.stock_id
        where stock.code = '000001.SZ'
          and daily.trade_date between '2024-01-01 00:00:00' and '2025-12-31 23:59:59'
        order by daily.trade_date
        """,
        engine=engine
    )
    return (dailies_df,)


@app.cell
def _(dailies_df):
    import pandas as pd

    dailies_df.rename(
        columns={'open': 'Open', 'close': 'Close', 'high': 'High', 'low': 'Low', 'volume': 'Volume'}, inplace=True
    )
    dailies_df['trade_date'] = pd.to_datetime(dailies_df['trade_date'], format='%Y-%m-%d')
    dailies_df.set_index('trade_date', inplace=True)
    dailies_df
    return


@app.cell
def _(dailies_df):
    import talib

    dailies_df['sma10'] = talib.SMA(dailies_df['Close'], timeperiod=10)
    dailies_df['sma30'] = talib.SMA(dailies_df['Close'], timeperiod=30)
    dailies_df['sma60'] = talib.SMA(dailies_df['Close'], timeperiod=60)
    dailies_df['sma120'] = talib.SMA(dailies_df['Close'], timeperiod=120)
    dailies_df
    return


@app.cell
def _(dailies_df):
    # 指标计算完成后截取后面指标的完整的部份使用
    target_dailies_df = dailies_df.loc['2025-01-01':'2025-12-31']
    target_dailies_df
    return (target_dailies_df,)


@app.cell
def _():
    from backtesting import Strategy
    from backtesting.lib import crossover

    class SmaCross(Strategy):
        def init(self):
            self.sma10 = self.I(lambda x: x, self.data.sma10)
            self.sma30 = self.I(lambda x: x, self.data.sma30)
            self.sma60 = self.I(lambda x: x, self.data.sma60)
            # self.sma120 = self.I(lambda x: x, self.data.sma120)

        def next(self):
            if self.sma60 > 0 and crossover(self.data.sma10, self.data.sma30):
                self.buy()
            elif self.position.size > 0 and crossover(self.data.sma30, self.data.sma10):
                self.position.close()
    return (SmaCross,)


@app.function
def stats_print(stats):
    indicator_name_mapping = {
        # 'Start': '回测开始时间',
        # 'End': '回测结束时间',
        # 'Duration': '回测持续时长',
        # 'Exposure Time [%]': '持仓时间占比（%）',
        'Equity Final [$]': '最终收益',
        'Equity Peak [$]': '峰值收益',
        'Return [%]': '总收益率（%）',
        'Buy & Hold Return [%]': '买入并持有收益率（%）',
        'Return (Ann.) [%]': '年化收益率（%）',
        'Volatility (Ann.) [%]': '年化波动率（%）',
        # 'CAGR [%]': '复合年均增长率（%）',
        # 'Sharpe Ratio': '夏普比率',
        'Sortino Ratio': '索提诺比率',
        'Calmar Ratio': '卡尔玛比率',
        # 'Alpha [%]': '阿尔法系数（%）',
        # 'Beta': '贝塔系数',
        'Max. Drawdown [%]': '最大回撤（%）',
        'Avg. Drawdown [%]': '平均回撤（%）',
        'Max. Drawdown Duration': '最大回撤持续时长',
        'Avg. Drawdown Duration': '平均回撤持续时长',
        '# Trades': '总交易次数',
        'Win Rate [%]': '胜率（%）',
        # 'Best Trade [%]': '最佳单笔交易收益率（%）',
        # 'Worst Trade [%]': '最差单笔交易收益率（%）',
        # 'Avg. Trade [%]': '平均单笔交易收益率（%）',
        # 'Max. Trade Duration': '单笔交易最长持有时长',
        # 'Avg. Trade Duration': '单笔交易平均持有时长',
        # 'Profit Factor': '盈利因子',
        # 'Expectancy [%]': '期望收益（%）',
        'SQN': '系统质量数',
        # 'Kelly Criterion': '凯利准则',
    }
    for k, v in stats.items():
        if k in indicator_name_mapping:
            cn_name = indicator_name_mapping.get(k, k)
            if isinstance(v, (int, float)):
                if "%" in cn_name or k in ['Sharpe Ratio', 'Sortino Ratio', 'Calmar Ratio', 'Profit Factor']:
                    formatted_value = f"{v:.2f}"
                elif "$" in cn_name:
                    formatted_value = f"{v:.2f}"
                elif "次数" in cn_name:
                    formatted_value = f"{v:.0f}"
                else:
                    formatted_value = f"{v:.4f}"
            else:
                formatted_value = str(v)
            print(f'{cn_name}: {formatted_value}')


@app.cell
def _(SmaCross, target_dailies_df):
    from backtesting import Backtest

    import backtesting._plotting as plotting
    from bokeh.colors.named import tomato, lime

    plotting.BULL_COLOR = tomato
    plotting.BEAR_COLOR = lime

    bt = Backtest(target_dailies_df, SmaCross, cash=100000, commission=.002, finalize_trades=True)
    stats = bt.run()
    stats_print(stats)
    return bt, stats


@app.cell
def _(stats):
    stats._trades
    # stats._equity_curve
    return


@app.cell
def _(bt):
    bt.plot()
    return


if __name__ == "__main__":
    app.run()
