# coding=utf-8
from __future__ import print_function, absolute_import
from gm.api import *
from MyTT import *


"""
示例策略仅供参考，不建议直接实盘使用。

本策略采用布林线进行均值回归交易。当价格触及布林线上轨的时候进行卖出，当触及下轨的时候，进行买入。
"""


# 策略中必须有init方法
def init(context):
    # 设置布林线的三个参数
    context.maPeriod = 26  # 计算BOLL布林线中轨的参数
    context.stdPeriod = 150  # 计算BOLL 标准差的参数
    context.stdRange = 1  # 计算BOLL 上下轨和中轨距离的参数
    # 设置要进行回测的合约
    context.symbol2 = 'SZSE.399673'  # 订阅&交易标的, 此处订阅的是600004 ['SHSE.600519', 'SHSE.600419'],
    context.symbol = 'SZSE.159949'
    context.symbolTwo = ['SZSE.399673','SZSE.159949']
    context.period = max(context.maPeriod, context.stdPeriod, context.stdRange) + 1  # 订阅数据滑窗长度
    # 订阅行情
    subscribe(symbols= context.symbolTwo, frequency='1d', count=context.period)


def on_bar(context, bars):
    # 获取数据滑窗，只要在init里面有订阅，在这里就可以取的到，返回值是pandas.DataFrame
    data2 = context.data(symbol=context.symbol, frequency='1d', count=context.period, fields='close,open,high,low,bob,eob')
    data = context.data(symbol=context.symbol2, frequency='1d', count=context.period, fields='close,open,high,low,bob,eob')

    # print(data2)
    #print(data.bob.values[-1])

    close = data.close.values
    low = data.low.values
    high = data.high.values
    dopen = data.open.values
    H1_5 = EMA(close, 8)
    H2_5 = EMA(H1_5, 20)
    # print('h1-5:', H1_5[-4:])
    # print('h2-5:', H2_5[-4:])
    H1H2_CROSS = CROSS(H1_5, H2_5)
    H2H1_CROSS = CROSS(H2_5, H1_5)
    # print('cross H1 H2：', H1H2_CROSS)
    # print('cross H2 H1', H2H1_CROSS)
    # qrld
    rsv = (close - LLV(low, 7)) / (HHV(high, 7) - LLV(low, 7)) * 100
    rsv = np.nan_to_num(rsv)
    # print('rsv :', rsv)
    Y0 = SMA(rsv, 3, 1)
    Y0 = np.nan_to_num(Y0)
    # print('Y0 :', Y0)
    Y1 = SMA(Y0, 3, 1)
    Y1 = np.nan_to_num(Y1)
    # print('Y1 :', Y1)
    CROSS_Y0_Y1 = CROSS(Y0, Y1)
    CROSS_Y1_Y0 = CROSS(Y1, Y0)
    RSV1 = (close - LLV(low, 38)) / (HHV(high, 38) - LLV(low, 38)) * 100
    RSV1 = np.nan_to_num(RSV1)
    # print('RSV1 :', RSV1)
    Y2 = SMA(RSV1, 5, 1)
    Y2 = np.nan_to_num(Y2)
    # print('Y2 :', Y2)
    Y3 = SMA(Y2, 10, 1)
    Y3 = np.nan_to_num(Y3)
    # print('Y3 :', Y3)

    # lq
    N = 1
    xopen = (REF(dopen, N) + REF(close, N)) / 2
    xclose = close
    xhigh = MAX(high, xopen)
    xlow = MIN(low, xopen)
    volality = MA(xhigh - xlow, 8)
    h_line = MA(xclose, 5) + volality / 2
    f_line = MA(xclose, 5) - volality / 2
    bu = CROSS(xclose, h_line)
    sel = CROSS(f_line, xclose)
    var1 = BARSLAST(bu)
    var2 = BARSLAST(sel)

    # 布林带上轨
    bollUpper = H1H2_CROSS 
    # 布林带下轨
    bollBottom = H2H1_CROSS

    # cross_kdj_up = cross_y0_y1[-1] and (Y0[-1] > Y1[-1]);

    a1 = (H1_5[-1]-H2_5[-1])/((H1_5[-1]+H2_5[-1])/2)
    b1 = (Y2[-1] - Y3[-1]) / 100
    c1 = (Y2[-1] + Y3[-1]) / 2


    if(CROSS_Y0_Y1[-1] == 0 or CROSS_Y1_Y0[-1] == 0):
        return
    else:
        #print('有信号：CROSS_Y0_Y1[-1]:'+str(CROSS_Y0_Y1[-1]))
        #print('有信号：CROSS_Y1_Y0[-1]:'+str(CROSS_Y1_Y0[-1]))
        pos = list(filter(lambda x:x['symbol']==context.symbol,get_position()))
        if(Y0[-1]>Y1[-1] and (b1>0) and ( a1>-0.02 or a1<0.02)):
            #BUY
            if( (a1<-0.04 or b1<-0.17)):
               # print('a#1<-0.04 or b1<-0.17 cant buy')
                return
            # 交易逻辑与下单
            # 当有持仓，且股价穿过BOLL上界的时候卖出股票。
    # # 当没有持仓，且股价穿过BOLL下界的时候买入股票。
            if not pos:
                cash_info = get_cash()  # 返回 DictLikeObject
                balance = cash_info['balance']  # 仍然可以通过键访问
              #  print("balance b:"+str(balance))
                volumeTmp = int(balance/data2.close.values[-1])
                volumeTmp = (volumeTmp // 100) * 100 - 500
                order_volume(symbol=context.symbol, volume=volumeTmp, side=OrderSide_Buy,
                                order_type=OrderType_Limit, position_effect=PositionEffect_Open, price=data2.close.values[-1])
        elif(Y0[-1]<=Y1[-1] and  ( a1>-0.02 or a1<0.02)):
            #sell
            # print("a1:"+str(a1))

            if(a1>0.05):
                print("a1>0.05")
                return
            if pos :
                cash_info = get_cash()  # 返回 DictLikeObject
                balance = cash_info['balance']  # 仍然可以通过键访问
                #print("balance s:"+str(balance))
               #print("pos:"+str(pos))
                volume_value = next((p['volume'] for p in pos if p['symbol'] == 'SZSE.159949'), None)
                volumeTmp = int(volume_value)
                order_volume(symbol=context.symbol, volume=volumeTmp, side=OrderSide_Sell,
                                order_type=OrderType_Limit, position_effect=PositionEffect_Close, price=data2.close.values[-1])
    # 交易逻辑与下单
    # 当有持仓，且股价穿过BOLL上界的时候卖出股票。
    # if pos and (bollBottom[-1] or bollUpper[-1]) and (H1_5[-1]<H2_5[-1]) :
    #     cash_info = get_cash()  # 返回 DictLikeObject
    #     balance = cash_info['balance']  # 仍然可以通过键访问
    #     print("balance s:"+str(balance))
    #     print("pos:"+str(pos))
    #     volume_value = next((p['volume'] for p in pos if p['symbol'] == 'SZSE.159949'), None)
    #     volumeTmp = int(volume_value)
    #     order_volume(symbol=context.symbol, volume=volumeTmp, side=OrderSide_Sell,
    #                     order_type=OrderType_Limit, position_effect=PositionEffect_Close, price=data2.close.values[-1])
    # # 当没有持仓，且股价穿过BOLL下界的时候买入股票。
    # elif not pos and (bollBottom[-1] or bollUpper[-1]) and ((H1_5[-1]>=H2_5[-1])):
    #     cash_info = get_cash()  # 返回 DictLikeObject
    #     balance = cash_info['balance']  # 仍然可以通过键访问
    #     print("balance b:"+str(balance))
    #     volumeTmp = int(balance/data2.close.values[-1])
    #     volumeTmp = (volumeTmp // 100) * 100 - 500
    #     order_volume(symbol=context.symbol, volume=volumeTmp, side=OrderSide_Buy,
    #                     order_type=OrderType_Limit, position_effect=PositionEffect_Open, price=data2.close.values[-1])

def on_order_status(context, order):
    # 标的代码
    symbol = order['symbol']
    # 委托价格
    price = order['price']
    # 委托数量
    volume = order['volume']
    # 目标仓位
    target_percent = order['target_percent']
    # 查看下单后的委托状态，等于3代表委托全部成交
    status = order['status']
    # 买卖方向，1为买入，2为卖出
    side = order['side']
    # 开平仓类型，1为开仓，2为平仓
    effect = order['position_effect']
    # 委托类型，1为限价委托，2为市价委托
    order_type = order['order_type']
    if status == 3:
        if effect == 1:
            if side == 1:
                side_effect = '开多仓'
            else:
                side_effect = '开空仓'
        else:
            if side == 1:
                side_effect = '平空仓'
            else:
                side_effect = '平多仓'
        order_type_word = '限价' if order_type==1 else '市价'
        print('{}:标的：{}，操作：以{}{}，委托价格：{}，委托数量：{}'.format(context.now,symbol,order_type_word,side_effect,price,volume))


def on_backtest_finished(context, indicator):
    print('*'*50)
    print('回测已完成，请通过右上角“回测历史”功能查询详情。')


if __name__ == '__main__':
    '''
        strategy_id策略ID,由系统生成
        filename文件名,请与本文件名保持一致
        mode实时模式:MODE_LIVE回测模式:MODE_BACKTEST
        token绑定计算机的ID,可在系统设置-密钥管理中生成
        backtest_start_time回测开始时间
        backtest_end_time回测结束时间
        backtest_adjust股票复权方式不复权:ADJUST_NONE前复权:ADJUST_PREV后复权:ADJUST_POST
        backtest_initial_cash回测初始资金
        backtest_commission_ratio回测佣金比例
        backtest_slippage_ratio回测滑点比例
        backtest_match_mode市价撮合模式，以下一tick/bar开盘价撮合:0，以当前tick/bar收盘价撮合：1
    '''
    run(strategy_id='3b7f1e3f-8e41-11f0-aae9-00155d8e2a12',
        filename='main.py',
        mode=MODE_BACKTEST,
        token='a130ee5e899369902d1e95d0a116b297ad1edbfb',
        backtest_start_time='2016-08-01 08:00:00',
        backtest_end_time='2025-09-19 15:30:00',
        backtest_adjust=ADJUST_PREV,
        backtest_initial_cash=10000,
        backtest_commission_ratio=0.0001,
        backtest_slippage_ratio=0.0001,
        backtest_match_mode=1)