2
Seguir
319
Seguidores

Fiebre del oro en la plataforma Inventor: un análisis práctico de un marco de trading de tendencias altamente flexible en Python

Creado el: 2025-08-22 17:25:52, Actualizado el: 2025-08-26 09:56:27
comments   0
hits   468

Suelo pasar tiempo en la plataforma de inventores y siempre encuentro tesoros. Hoy encontré a un joven de 21 años.Estrategia de tendenciaAdmiro la exquisita y perfecta estructura de código del autor original, y su gran flexibilidad. La estrategia original es la versión JS, que ha sido reescrita para comodidad de los usuarios de Python.

Sinceramente, muchos principiantes dan muchos rodeos al iniciarse en el trading cuantitativo. A menudo se encuentran con problemas como órdenes fallidas, pérdidas por una mala gestión del riesgo y pérdida de datos tras reiniciar una estrategia. Más tarde, me di cuenta de la importancia de un buen marco de referencia, que puede ayudarnos a evitar muchos obstáculos. Este marco de estrategia de tendencia es una herramienta muy valiosa. Es más que una simple estrategia de trading; es más bien una caja de herramientas que proporciona funciones básicas pero cruciales como la colocación de órdenes, las órdenes de stop-loss y la gestión de datos. Solo necesitas centrarte en las preguntas clave de “cuándo comprar” y “cuándo vender”. Además, el marco es muy abierto, lo que te permite cambiar fácilmente la EMA por el MACD, el RSI o cualquier otro indicador que prefieras. ¿Quieres seguir las tendencias? Sin problema. ¿Quieres probar la reversión a la media? ¿Incluso combinar varios indicadores? Por supuesto. Esta flexibilidad es increíblemente útil; puedes modificar el mismo código para experimentar con diferentes ideas.

Hoy comparto este marco con la esperanza de que sea útil para quienes exploran la inversión cuantitativa. A continuación, se presenta una introducción detallada de cada componente de este marco, que creo les resultará útil.

Estructura y función del marco

En comparación con las múltiples funciones independientes utilizadas en los marcos de negociación de múltiples materias primas, este marco intenta organizar y gestionar las distintas partes de una estrategia mediante un formato de clase. Este diseño orientado a objetos no solo mejora la mantenibilidad y la escalabilidad del código, sino que también hace que los componentes de la estrategia sean más modulares, lo que facilita los ajustes y optimizaciones posteriores. El marco consta principalmente de las siguientes secciones, cada una con su propia función específica, lo que garantiza la flexibilidad y la practicidad de la estrategia.

Inicialización y configuración

función init

  • Las funciones__init__Esta función es el método de inicialización de la clase de estrategia, responsable de establecer la configuración básica de la estrategia, inicializar variables y obtener información del mercado. Esta función garantiza que los parámetros necesarios se configuren antes de ejecutar la estrategia, garantizando así la fluidez de las operaciones de trading posteriores.
  • Los pasos
    1. Configuración básica:Establezca la moneda comercial, el tipo de contrato, las reglas de toma de ganancias y stop-loss, etc.
    2. Información del mercado:Obtener la exactitud del precio y la exactitud de la cantidad del contrato para garantizar la legalidad del pedido.
    3. Inicializando variables:Incluye juicios de tendencias, parámetros de stop-profit y stop-loss, variables estadísticas, etc., para ayudar a las estrategias a tomar decisiones basadas en las condiciones del mercado.
    4. Configuración de Exchange:Configure la interfaz API de intercambio de acuerdo con la información del mercado, como la configuración del margen, la precisión, etc.

función initDatas

  • Las funciones: Inicializa los datos cuando se ejecuta la estrategia, incluidos los activos de la cuenta, las estadísticas de ganancias, etc.
  • Los pasos
    1. Ahorre tiempo de ejecución de la política.
    2. Leer datos del usuario local.
    3. Inicializar los activos de la cuenta, las estadísticas de ingresos y otros datos.
    4. Comprueba si la toma de ganancias y la toma de ganancias con devolución de llamada están habilitadas al mismo tiempo.

Gestión y almacenamiento de datos

Función saveStrategyRunTime

  • Las funciones:Guarde la hora de inicio de la política para estadísticas y seguimiento posteriores.
  • Los pasos
    1. Compruebe si el tiempo de ejecución se guarda localmente.
    2. Si no se guarda, registre la hora actual y guárdela localmente.
    3. Si se ha guardado, lea la hora guardada localmente.

Función setStrategyRunTime

  • Las funciones:Establezca la hora de inicio de la política y guárdela en el almacenamiento local.
  • Los pasos
    1. Usando la plataforma_GLa función guarda localmente la marca de tiempo pasada.
    2. Actualice la hora de inicio de ejecución en los datos de la política.

Función getDaysFromTimeStamp

  • Las funciones: Calcula la diferencia en días entre dos marcas de tiempo para calcular la duración de la ejecución de la política.
  • Los pasos
    1. Comprueba si la hora de finalización es anterior a la hora de inicio y devuelve 0 si es así.
    2. Calcula la diferencia en segundos entre dos marcas de tiempo y la convierte a días.
    3. Devuelve la diferencia en días.

Función saveUserDatasLocal

  • Las funciones:Guarde datos clave durante la ejecución de la política en la computadora local para que puedan restaurarse cuando se reinicie la política.
  • Los pasos
    1. Cuenta de paquetes de activos, estadísticas de ingresos y otros datos.
    2. Usando la plataforma_GLa función guarda los datos localmente.

Función readUserDataLocal

  • Las funciones:Lea los datos del usuario guardados localmente para recuperarlos cuando se reinicie la política.
  • Los pasos
    1. Compruebe si hay datos guardados localmente.
    2. En caso contrario, inicialice los datos y guárdelos localmente.
    3. Si es así, se lee y se carga en la política.

Función clearUserDataLocal

  • Las funciones:Borra datos de usuario guardados localmente, generalmente se utilizan para restablecer políticas o depurar.
  • Los pasos
    1. Usando la plataforma_GLa función borra los datos locales.
    2. Operaciones de limpieza de troncos.

Interacción de políticas y procesamiento de comandos

Función runCmd

  • Las funciones:Procesa comandos enviados por los usuarios a través de la interfaz interactiva, como borrar datos locales, modificar la cantidad del pedido, etc.
  • Los pasos
    1. Obtener el comando enviado por el usuario.
    2. Ejecutar operaciones correspondientes según el tipo de comando, como borrar datos locales, modificar la cantidad del pedido, etc.
    3. Registrar los resultados de la ejecución del comando.

Gestión de órdenes y comercio

Función orderDirectly

  • Las funciones:Realizar pedidos directamente en función de la dirección y el precio, apoyando las operaciones de apertura y cierre.
  • Los pasos
    1. Seleccione la función comercial según la dirección (comprar o vender).
    2. Establecer la dirección comercial.
    3. Ejecutar la operación de orden y devolver el resultado.

Función openLong

  • Las funciones:Abra una posición larga y coloque una orden basada en precio y cantidad.
  • Los pasos
    1. Calcular la cantidad real del pedido.
    2. LlamarorderDirectlyLa función realiza una operación de compra.

Función openShort

  • Las funciones:Abra una posición corta y realice un pedido en función del precio y la cantidad.
  • Los pasos
    1. Calcular la cantidad real del pedido.
    2. LlamarorderDirectlyLa función realiza una operación de venta.

Función coverLong

  • Las funciones:Cerrar posiciones largas y realizar órdenes en función del precio y la cantidad.
  • Los pasos
    1. LlamarorderDirectlyLa función realiza una operación de venta.

Función coverShort

  • Las funciones:Cerrar posición corta y realizar pedido en función del precio y la cantidad.
  • Los pasos
    1. LlamarorderDirectlyLa función realiza una operación de compra.

Función getRealOrderSize

  • Las funciones:Recalcule la cantidad real del pedido en función del precio y la cantidad, y admita la realización de pedidos en función de la relación de margen.
  • Los pasos
    1. Calcule la cantidad real del pedido en función de si el pedido se realiza de acuerdo con la relación de margen.
    2. Devuelve la cantidad de pedido calculada.

Gestión de riesgos y estadísticas de rentabilidad

Función getSinglePositionMargin

  • Las funciones:Calcula el margen ocupado por una sola posición.
  • Los pasos
    1. El margen se calcula en función de la dirección y la cantidad de la posición.
    2. Devuelve el resultado del cálculo.

Función getSinglePositionProfit

  • Las funciones:Calcular los ingresos y el rendimiento de una única posición.
  • Los pasos
    1. Calcular el beneficio basándose en la dirección de la posición y el precio actual.
    2. Devuelve las ganancias y los rendimientos.

Función calculateForcedPrice

  • Las funciones: Calcula el precio de liquidación de una posición.
  • Los pasos
    1. El precio de liquidación se calcula en función de la dirección de la posición y el saldo de la cuenta.
    2. Devuelve el resultado del cálculo.

Función getMaxOrderSize

  • Las funciones:Calcular la cantidad máxima del pedido.
  • Los pasos
    1. La cantidad máxima de pedido que se puede realizar se calcula en función del saldo de la cuenta y el apalancamiento.
    2. Devuelve el resultado del cálculo.

Función getAccountAsset

  • Las funciones: Calcula los activos totales de la cuenta, incluidas las posiciones y el saldo disponible.
  • Los pasos
    1. Calcular el patrimonio total en función de las posiciones y el saldo de la cuenta.
    2. Devuelve el resultado del cálculo.

función calcularBeneficio

  • Las funciones:Calcular y registrar los rendimientos de la estrategia.
  • Los pasos
    1. Calcular la diferencia entre la rentabilidad total actual y los activos iniciales.
    2. Registrar las ganancias y actualizar las variables estadísticas.
    3. Guarde los datos de ingresos localmente.

Función isEnoughAssetToOrder

  • Las funciones:Verifique si los fondos de la cuenta son suficientes para realizar un pedido.
  • Los pasos
    1. Obtener información del saldo de la cuenta.
    2. Calcule los fondos necesarios según el tipo de moneda de la transacción (basada en USDT o en monedas).
    3. Verifique si el saldo de la cuenta cumple con los requisitos del pedido.
    4. Devuelve un valor booleano que indica si los fondos son suficientes.

Juicio de tendencias y lógica comercial

Función runInKLinePeriod

  • Las funciones:Determinar si se debe ejecutar la lógica de la estrategia en función del ciclo de la línea K.
  • Los pasos
    1. Compruebe si se ha procesado la línea K actual.
    2. Si no se procesa, márquelo como procesado y devuélvalo.True, de lo contrario regresarFalse

Función trendJudgment (módulo principal de juicio de tendencias)

  • Las funcionesDetermine la tendencia actual con base en indicadores técnicos. Este es el módulo más flexible de todo el framework. Los usuarios pueden reemplazar diferentes indicadores técnicos para determinar la tendencia según sus necesidades.
  • Implementación actual:Utilice la EMA (promedio móvil exponencial) combinada con la desviación estándar para determinar la tendencia
  • Escalabilidad:Esta función está diseñada como un módulo enchufable y los usuarios pueden reemplazarlo fácilmente con otros indicadores técnicos, como:
    • RSI (Índice de fuerza relativa):Evaluación de condiciones de sobrecompra y sobreventa
    • MACD (Convergencia-Divergencia de Media Móvil):Identificar puntos de inflexión de tendencias
    • Bandas de Bollinger:Juicio de tendencia basado en la volatilidad del precio
    • Indicador KDJ:Juicio combinado de impulso y tendencia
    • Combinación de varios indicadores:Se pueden combinar varios indicadores para obtener una evaluación de tendencias más precisa
  • Los pasos
    1. Calcula el indicador EMA y determina si el precio lo cruza.
    2. Determinar si está en una tendencia basándose en la desviación estándar.
    3. Devuelve la tendencia actual (larga, corta o rango).

función stopLoss

  • Las funciones:Ejecute la operación de stop-loss de acuerdo con las reglas de stop-loss.
  • Los pasos
    1. Verificar si la posición alcanza las condiciones de stop loss.
    2. Si se alcanza, se cerrará la posición y se registrará la información del stop loss.

función takeProfit

  • Las funciones:Ejecutar la operación de toma de ganancias de acuerdo con las reglas de toma de ganancias.
  • Los pasos
    1. Verificar si la posición cumple las condiciones de toma de ganancias.
    2. Si se alcanza, se cerrará la posición y se registrará la información de toma de ganancias.

función de seguimiento de TakeProfit

  • Las funciones:Ejecute la operación de toma de ganancias de acuerdo con las reglas de toma de ganancias de devolución de llamada.
  • Los pasos
    1. Verifique si la posición cumple las condiciones de activación de la devolución de llamada para tomar ganancias.
    2. Si se alcanza, se cerrará la posición y se registrará la información de toma de ganancias.

función de orden

  • Las funciones:Ejecutar operación de orden basándose en los resultados del juicio de tendencia.
  • Los pasos
    1. Verificar posiciones actuales.
    2. Abrir o cerrar posiciones en función de los resultados del juicio de tendencias.

Lógica central de la estrategia

Función trendStrategy

  • Las funciones:La función lógica central de la estrategia, responsable de ejecutar el juicio de tendencia, stop loss y take profit, callback take profit y operaciones de órdenes.
  • Los pasos
    1. Obtener datos del mercado:Obtenga información actual del mercado, información de posición, información de cuenta y datos de la línea K.
    2. Comprobar posiciones:Asegúrese de que no se mantengan posiciones largas o cortas simultáneamente, de lo contrario se lanzará una excepción.
    3. Interacción estratégica:Procesa comandos enviados por los usuarios a través de la interfaz interactiva.
    4. Impresión de información de la barra de estado:Actualizar e imprimir el estado de la operación de la estrategia, la información de la cuenta y el estado de la posición.
    5. Detener el daño:Verificar y ejecutar operaciones de stop-loss de acuerdo con las reglas de stop-loss.
    6. Se detiene.:Verificar y ejecutar operaciones de toma de ganancias de acuerdo con las reglas de toma de ganancias.
    7. Tomar ganancias en la devolución de llamada:Verifique y ejecute operaciones de toma de ganancias de acuerdo con las reglas de toma de ganancias de devolución de llamada.
    8. Comprobación del ciclo de la línea K:Asegúrese de que la lógica de la estrategia se ejecute de acuerdo con el ciclo de la línea K.
    9. Juzgar las tendencias:Determinar la tendencia actual (larga, corta u oscilante) basándose en indicadores técnicos.
    10. Realizar un pedido:Abrir o cerrar una posición según los resultados del juicio de tendencia.

Monitoreo de estado y salida de registros

Función printLogStatus

  • Las funciones:Imprima el estado de la operación de la estrategia, la información de la cuenta y el estado de la posición.
  • Los pasos
    1. Construir datos tabulares de la descripción general de la estrategia, los fondos de la cuenta y las posiciones.
    2. usarLogStatusLa función envía los datos de la tabla a la barra de estado.

Función principal y ejecución de la estrategia

función principal

  • Las funciones:La función principal de la estrategia, responsable de inicializar la estrategia y recorrer la lógica de la estrategia.
  • Los pasos
    1. Inicializar el entorno de simulación de intercambio.
    2. Cree una instancia de estrategia e inicialice los datos.
    3. La lógica de la estrategia se ejecuta en un bucle, verificando la situación del mercado y ejecutando operaciones comerciales a intervalos regulares.

Características del marco

  1. Juicio de tendencia flexibleUtilizando la EMA y la desviación estándar, esta estrategia permite determinar con flexibilidad las tendencias del mercado y es aplicable a diversos entornos de mercado. Esta función es solo un ejemplo; los usuarios pueden usar diferentes indicadores técnicos (como el RSI, el MACD, las Bandas de Bollinger, etc.) para determinar las tendencias según sea necesario.
  2. Diversos mecanismos de stop-loss y take-profit:Admite stop loss de porcentaje fijo, take profit y callback take profit para satisfacer las necesidades de los traders con diferentes preferencias de riesgo.
  3. Gestión de datos locales:Los datos de operación de la política y los datos del usuario se guardan localmente para garantizar que la política pueda restaurarse a su estado anterior después del reinicio.
  4. Comandos interactivos:Admite la interacción con políticas a través de la línea de comandos, lo que facilita a los usuarios ajustar los parámetros de políticas o realizar operaciones específicas.

Aplicabilidad

Este marco no solo es aplicable al mercado de divisas digitales, sino que también se puede utilizar en eltrendJudgmentEl marco se puede ampliar para adaptarse a diferentes estrategias de negociación. Además, se puede modificar específicamente para el mercado spot o contratos multivariantes, con gran flexibilidad y escalabilidad.

  1. Soporte del mercado al contado:Actualmente, este marco está orientado principalmente al mercado de contratos y en el futuro podrá ampliarse para respaldar estrategias comerciales en el mercado al contado.
  2. Contratos multiproductoAl agregar soporte para múltiples contratos, las estrategias pueden monitorear y comercializar múltiples monedas digitales simultáneamente, mejorando la utilización del capital.
  3. Integración del aprendizaje automáticoCombinado con algoritmos de aprendizaje automático, mejora aún más la precisión del juicio de tendencias y el nivel de inteligencia de las estrategias.
  4. Optimización de la gestión de riesgos:Optimizar aún más los mecanismos de gestión de riesgos, como el ajuste dinámico de los ratios de apalancamiento y los mecanismos de stop-loss y take-profit de varios niveles, para mejorar la solidez de la estrategia.

Resumir

Como sistema de trading automatizado integral y altamente flexible, este framework es ideal para el trading de tendencias en el mercado de criptomonedas. Gracias a su continua optimización y expansión, se espera que se convierta en una herramienta valiosa para los operadores de criptomonedas en el futuro, ayudándoles a desarrollar mejor sus propias estrategias cuantitativas. El “Marco de Trading de Estrategias de Tendencia de Criptomonedas” cuenta con una estructura integral. Si bien su código es relativamente extenso, abarca esencialmente los módulos funcionales básicos necesarios para el trading de tendencias desde una perspectiva real. Por lo tanto, este framework tiene un gran valor de referencia y una gran relevancia práctica, tanto para el aprendizaje de estrategias de trading como para su aplicación práctica. Su completa funcionalidad y flexibilidad le permiten adaptarse a diversos entornos de mercado, ofreciendo un sólido soporte.

La Plataforma Inventor es un tesoro de conocimientos y estrategias de trading cuantitativo, cada una de las cuales refleja la sabiduría y la experiencia de sus desarrolladores. Les invitamos a explorar valiosas estrategias y técnicas de trading aquí. Gracias a todos nuestros usuarios innovadores y participativos. Gracias a sus contribuciones, esta plataforma se ha convertido en un espacio vital para el aprendizaje y el intercambio en trading cuantitativo, ayudando a todos a mejorar sus habilidades y conocimientos.

”`python “‘backtest start: 2024-11-26 00:00:00 end: 2024-12-03 00:00:00 period: 1d basePeriod: 1d exchanges: [{“eid”:“Futures_Binance”,“currency”:“BTC_USDT”}] “’

import json, talib import numpy as np

class TrendStrategy: def init(self): # 基本设置 self._Currency = TradeCurrency self._Interval = Interval self._UseQuarter = UseQuarter self._UseContract = TradeCurrency + (‘.swap’ if self._UseQuarter else ‘.quarter’) self._OnlyTrendJudgment = OnlyTrendJudgment self._EnableMessageSend = EnableMessageSend # 趋势判断 self._RunInKLinePeriod = RunInKLinePeriod self._KLinePeriod = KLinePeriod self._EmaLength = EmaLength self._EmaCoefficient = EmaCoefficient self._UseStddev = UseStddev self._UseRecordsMiddleValue = UseRecordsMiddleValue self._StddevLength = StddevLength self._StddevDeviations = StddevDeviations # 下单设置 self._MarginLevel = MarginLevel self._OrderSize = OrderSize self._OrderByMargin = OrderByMargin self._OrderMarginPercent = OrderMarginPercent self._PricePrecision = None self._AmountPrecision = None self._OneSizeInCurrentCoin = None self._QuarterOneSizeValue = None # 止盈止损 self._UseStopLoss = UseStopLoss self._StopLossPercent = StopLossPercent self._UseTakeProfit = UseTakeProfit self._TakeProfitPercent = TakeProfitPercent self._UseTrackingTakeProfit = UseTrackingTakeProfit self._UsePositionRetracement = UsePositionRetracement self._TakeProfitTriggerPercent = TakeProfitTriggerPercent self._CallBakcPercent = CallBakcPercent

    # 策略变量
    self._LastBarTime = 0
    self._TrendWhenTakeProfitOrStopLoss = 0
    self._HadStopLoss = False
    self._TriggeredTakeProfit = False
    self._PeakPriceInPosition = 0
    self._HadTakeProfit = False
    self._PriceCrossEMAStatus = 0

    # 统计变量
    self._InitAsset = 0
    self._ProfitLocal = 0
    self._TakeProfitCount = 0
    self._TradeCount = 0
    self.StrategyRunTimeStampString = "strategy_run_time"
    self._StrategyDatas = {"start_run_timestamp": 0, "others": ""}
    self._UserDatas = None

    # 相对固定参数
    self._MaintenanceMarginRate = 0.004
    self._TakerFee = 0.0005
    self._IsUsdtStandard = False

    # 获取合约信息
    ticker = _C(exchange.GetTicker, self._UseContract)
    marketInfo = exchange.GetMarkets()[self._UseContract]
    Log('获取市场信息:', marketInfo)
    self._PricePrecision = marketInfo['PricePrecision']
    self._AmountPrecision = marketInfo['AmountPrecision']
    self._OneSizeInCurrentCoin = marketInfo['CtVal']
    self._QuarterOneSizeValue = marketInfo['CtVal']

    exchange.SetCurrency(self._Currency)
    exchange.SetMarginLevel(self._UseContract, self._MarginLevel)
    exchange.SetPrecision(self._PricePrecision, self._AmountPrecision)

    # 初始化数据
def initDatas(self):

    self.saveStrategyRunTime()
    self.readUserDataLocal()

    self._InitAsset = self._UserDatas["init_assets"]
    self._ProfitLocal = self._UserDatas["profit_local"]
    self._TakeProfitCount = self._UserDatas["take_profit_count"]
    self._TradeCount = self._UserDatas["trade_count"]

    if self._OrderByMargin:
        self.getRealOrderSize(-1, self._OrderSize)
        Log("已经重新计算下单张数:", self._OrderSize)
    if self._UseTakeProfit and self._UseTrackingTakeProfit:
        raise Exception("止盈和回调止盈不能同时使用!")

# 设置合约
def setContract(self):
    self._IsUsdtStandard = "USDT" in self._Currency

    exchange.SetCurrency(self._Currency)
    if self._UseQuarter:
        exchange.SetContractType("quarter")
    else:
        exchange.SetContractType("swap")

# 保存程序起始运行时间 秒级时间戳
def saveStrategyRunTime(self):
    local_data_strategy_run_time = _G(self.StrategyRunTimeStampString)

    if local_data_strategy_run_time is None:
        self._StrategyDatas["start_run_timestamp"] = Unix()
        _G(self.StrategyRunTimeStampString, self._StrategyDatas["start_run_timestamp"])
    else:
        self._StrategyDatas["start_run_timestamp"] = local_data_strategy_run_time

# 设置程序起始运行时间 秒级时间戳
def setStrategyRunTime(self, timestamp):
    _G(self.StrategyRunTimeStampString, timestamp)
    self._StrategyDatas["start_run_timestamp"] = timestamp

# 计算两个时间戳之间的天数,参数是秒级时间戳
def getDaysFromTimeStamp(self, start_time, end_time):
    if end_time < start_time:
        return 0

    return (end_time - start_time) // (60 * 60 * 24)

# 保存数据到本地
def saveUserDatasLocal(self):
    self._UserDatas = {
        "init_assets": self._InitAsset,
        "profit_local": self._ProfitLocal,
        "take_profit_count": self._TakeProfitCount,
        "trade_count": self._TradeCount
    }
    # 存储到本地
    _G(exchange.GetLabel(), self._UserDatas)
    Log("已把所有数据保存到本地.")

# 读取用户本地数据,程序启动时候运行一次
def readUserDataLocal(self):
    user_data = _G(exchange.GetLabel())
    if user_data is None:
        self._InitAsset = self.getAccountAsset(_C(exchange.GetPosition), _C(exchange.GetAccount), _C(exchange.GetTicker))
        self._UserDatas = {
            "init_assets": self._InitAsset,
            "profit_local": 0,
            "take_profit_count": 0,
            "trade_count": 0
        }
    else:
        self._UserDatas = user_data

# 清除用户本地数据,交互按钮点击运行
def clearUserDataLocal(self):
    _G(exchange.GetLabel(), None)
    Log(exchange.GetLabel(), ":已清除本地数据.")

# 策略交互
def runCmd(self):
    cmd = GetCommand()

    if cmd:
        # 检测交互命令
        Log("接收到的命令:", cmd, "#FF1CAE")
        if cmd.startswith("ClearLocalData:"):
            # 清除本地数据
            self.clearUserDataLocal()
        elif cmd.startswith("SaveLocalData:"):
            # 保存数据到本地
            self.saveUserDatasLocal()
        elif cmd.startswith("ClearLog:"):
            # 清除日志
            log_reserve = cmd.replace("ClearLog:", "")
            LogReset(int(log_reserve))
        elif cmd.startswith("OrderSize:"):
            # 修改下单张数
            if self._OrderByMargin:
                Log("已经使用保证金数量来下单,无法直接修改下单数量!")
            else:
                order_size = int(cmd.replace("OrderSize:", ""))
                self._OrderSize = order_size
                Log("下单张数已经修改为:", self._OrderSize)
        elif cmd.startswith("OrderMarginPercent:"):
            # 修改下单保证金百分比
            if self._OrderByMargin:
                order_margin_percent = float(cmd.replace("OrderMarginPercent:", ""))
                self._OrderMarginPercent = order_margin_percent
                Log("下单保证金百分比:", self._OrderMarginPercent, "%")
            else:
                Log("没有打开根据保证金数量下单,无法修改下单保证金百分比!")

# 交易函数
def orderDirectly(self, distance, price, amount):
    tradeFunc = None

    if amount <= 0:
        raise Exception("设置的参数有误,下单数量已经小于0!")

    if distance == "buy":
        tradeFunc = exchange.Buy
    elif distance == "sell":
        tradeFunc = exchange.Sell
    elif distance == "closebuy":
        tradeFunc = exchange.Sell
    else:
        tradeFunc = exchange.Buy

    exchange.SetDirection(distance)
    return tradeFunc(price, amount)

def openLong(self, price, amount):
    real_amount = self.getRealOrderSize(price, amount)
    return self.orderDirectly("buy", price, real_amount)

def openShort(self, price, amount):
    real_amount = self.getRealOrderSize(price, amount)
    return self.orderDirectly("sell", price, real_amount)

def coverLong(self, price, amount):
    return self.orderDirectly("closebuy", price, amount)

def coverShort(self, price, amount):
    return self.orderDirectly("closesell", price, amount)

# 重新计算下单数量
def getRealOrderSize(self, price, amount):
    real_price = price if price != -1 else _C(exchange.GetTicker).Last
    if self._OrderByMargin:
        if self._IsUsdtStandard:

            self._OrderSize = _N(self._InitAsset * (self._OrderMarginPercent / 100) / real_price * self._MarginLevel / self._OneSizeInCurrentCoin, self._AmountPrecision)  # u本位数量(杠杆放大数量)

        else:
            self._OrderSize = _N(self._InitAsset * (self._OrderMarginPercent / 100) * self._MarginLevel * real_price / self._QuarterOneSizeValue, self._AmountPrecision)  # 币本位数量(杠杆放大数量)
    else:
        self._OrderSize = amount
    return self._OrderSize

# 获取单个持仓占用保证金
def getSinglePositionMargin(self, position, ticker):
    position_margin = 0

    if len(position) > 0:
        if self._IsUsdtStandard:
            position_margin = position[0].Amount * self._OneSizeInCurrentCoin * ticker.Last / self._MarginLevel
        else:
            position_margin = position[0].Amount * self._QuarterOneSizeValue / ticker.Last / self._MarginLevel

    return position_margin

# 获取单向持仓的收益和收益%
def getSinglePositionProfit(self, position, ticker):
    if len(position) == 0:
        return [0, 0]

    price = ticker.Last
    position_margin = self.getSinglePositionMargin(position, ticker)

    position_profit_percent = (price - position[0].Price) / position[0].Price * self._MarginLevel if position[0].Type == PD_LONG else (position[0].Price - price) / position[0].Price * self._MarginLevel
    position_profit = position_margin * position_profit_percent

    return [position_profit, position_profit_percent]

# 计算强平价格
def calculateForcedPrice(self, account, position, ticker):
    position_profit = 0
    total_avail_balance = 0
    forced_price = 0

    position_margin = self.getSinglePositionMargin(position, ticker)
    [position_profit, position_profit_percent] = self.getSinglePositionProfit(position, ticker)

    if self._IsUsdtStandard:
        total_avail_balance = account.Balance + position_margin + account.FrozenBalance - position_profit if position_profit > 0 else account.Balance + position_margin + account.FrozenBalance
        if position[0].Type == PD_LONG:
            forced_price = ((self._MaintenanceMarginRate + self._TakerFee) * self._MarginLevel * account.FrozenBalance - total_avail_balance) / self._OneSizeInCurrentCoin + (position[0].Amount * position[0].Price) / (position[0].Amount - (self._MaintenanceMarginRate + self._TakerFee) * position[0].Amount)
        else:
            forced_price = ((self._MaintenanceMarginRate + self._TakerFee) * self._MarginLevel * account.FrozenBalance - total_avail_balance) / self._OneSizeInCurrentCoin - (position[0].Amount * position[0].Price) / (-1 * position[0].Amount - (self._MaintenanceMarginRate + self._TakerFee) * position[0].Amount)
    else:
        total_avail_balance = account.Stocks + position_margin + account.FrozenStocks - position_profit if position_profit > 0 else account.Stocks + position_margin + account.FrozenStocks
        if position[0].Type == PD_LONG:
            forced_price = (self._MaintenanceMarginRate * position[0].Amount + position[0].Amount) / (total_avail_balance / self._QuarterOneSizeValue + position[0].Amount / position[0].Price)
        else:
            forced_price = (self._MaintenanceMarginRate * position[0].Amount - position[0].Amount) / (total_avail_balance / self._QuarterOneSizeValue - position[0].Amount / position[0].Price)

    if forced_price < 0:
        forced_price = 0

    return forced_price

# 计算最大可下单张数
def getMaxOrderSize(self, margin_level, ticker, account):
    max_order_size = 0

    if self._IsUsdtStandard:
        max_order_size = account.Balance * margin_level / (self._OneSizeInCurrentCoin * ticker.Last)
    else:
        max_order_size = account.Stocks * ticker.Last / self._QuarterOneSizeValue * margin_level

    return _N(max_order_size, self._AmountPrecision)

# 获取账户资产
def getAccountAsset(self, position, account, ticker):
    # 计算不同情况下的账户初始资产
    account_asset = 0
    position_margin = self.getSinglePositionMargin(position, ticker)

    if self._IsUsdtStandard:
        if len(position) > 0:
            account_asset = account.Balance + account.FrozenBalance + position_margin
        else:
            account_asset = account.Balance + account.FrozenBalance
    else:
        if len(position) > 0:
            account_asset = account.Stocks + account.FrozenStocks + position_margin
        else:
            account_asset = account.Stocks + account.FrozenStocks

    return account_asset

# 收益统计
def calculateProfit(self, ticker):
    # 重新获取一下账户持仓与资产
    position = _C(exchange.GetPosition)
    account = _C(exchange.GetAccount)
    # 当前总收益 - 上一次总收益 = 本次的收益
    current_profit = (self.getAccountAsset(position, account, ticker) - self._InitAsset) - self._ProfitLocal
    self._ProfitLocal += current_profit

    if current_profit > 0:
        self._TakeProfitCount += 1
    self._TradeCount += 1

    LogProfit(_N(self._ProfitLocal, 4), "        本次收益:", _N(current_profit, 6))
    self.saveUserDatasLocal()

# 是否还够资金下单
def isEnoughAssetToOrder(self, order_size, ticker):
    is_enough = True
    account = _C(exchange.GetAccount)

    if self._IsUsdtStandard:
        if account.Balance < order_size * ticker.Last * self._OneSizeInCurrentCoin / self._MarginLevel:
            is_enough = False
    else:
        if account.Stocks < order_size * self._QuarterOneSizeValue / ticker.Last / self._MarginLevel:
            is_enough = False

    return is_enough

# 按照K线周期运行策略核心
def runInKLinePeriod(self, records):
    bar_time = records[-1].Time
    if self._RunInKLinePeriod and self._LastBarTime == bar_time:
        return False

    self._LastBarTime = bar_time
    return True

# 趋势判断模块(可编辑具体指标)
def trendJudgment(self, records):
    # 检查价格是否穿过均线
    def checkPriceCrossEma(price, ema_value):
        if self._PriceCrossEMAStatus == 0:
            if price <= ema_value:
                self._PriceCrossEMAStatus = -1
            else:
                self._PriceCrossEMAStatus = 1
        elif (self._PriceCrossEMAStatus == -1 and price >= ema_value) or (self._PriceCrossEMAStatus == 1 and price <= ema_value):
            self._PriceCrossEMAStatus = 2  # 完成穿过

    # EMA的多空判断
    ema_long = False
    ema_short = False
    price = records[-2].Close  # 已经收盘的K线的收盘价格
    ema = TA.EMA(records, self._EmaLength)
    ema_value = ema[-2]  # 收盘K线对应ema值
    ema_upper = ema_value * (1 + self._EmaCoefficient)
    ema_lower = ema_value * (1 - self._EmaCoefficient)

    checkPriceCrossEma(price, ema_value)
    if price > ema_upper:
        ema_long = True
    elif price < ema_lower:
        ema_short = True

    # 标准差判断
    in_trend = False
    if self._UseStddev:
        records_data = []
        for i in range(len(records)):
            records_data.append((records[i].High + records[i].Low) / 2 if self._UseRecordsMiddleValue else records[i].Close)

        records_data = np.array(records_data)  # 将 list 转换为 np.array
        stddev = np.std(records_data, ddof=1)  # 使用 numpy 计算标准差
        if stddev > self._StddevDeviations:
            in_trend = True
    else:
        in_trend = True

    # 趋势判断
    long = in_trend and ema_long 
    short = in_trend and ema_short

    if long:
        Log("当前趋势为:多", self._EnableMessageSend and "@" or "#00FF7F")
    elif short:
        Log("当前趋势为:空", self._EnableMessageSend and "@" or "#FF0000")
    else:
        Log("当前趋势为:震荡", self._EnableMessageSend and "@" or "#007FFF")

    return [long, short]

# 止损
def stopLoss(self, position, ticker):
    stop_loss_price = 0
    price = ticker.Last

    if len(position) == 1 and self._UseStopLoss:
        if position[0].Type == PD_LONG:
            stop_loss_price = position[0].Price * (1 - self._StopLossPercent / 100)
            if price < stop_loss_price:
                self.coverLong(-1, position[0].Amount)
                self.calculateProfit(ticker)
                self._TrendWhenTakeProfitOrStopLoss = 1
                self._HadStopLoss = True
                Log("多单止损。止损价格:", _N(stop_loss_price, 6), ", 持仓价格:", _N(position[0].Price), self._EnableMessageSend and "@" or "#FF1CAE")
        elif position[0].Type == PD_SHORT:
            stop_loss_price = position[0].Price * (1 + self._StopLossPercent / 100)
            if price > stop_loss_price:
                self.coverShort(-1, position[0].Amount)
                self.calculateProfit(ticker)
                self._TrendWhenTakeProfitOrStopLoss = -1
                self._HadStopLoss = True
                Log("空单止损。止损价格:", _N(stop_loss_price, 6), ", 持仓价格:", _N(position[0].Price), self._EnableMessageSend and "@" or "#FF1CAE")

# 止盈
def takeProfit(self, position, ticker):
    take_profit_price = 0
    price = ticker.Last

    if len(position) == 1 and self._UseTakeProfit:
        if position[0].Type == PD_LONG:
            take_profit_price = position[0].Price * (1 + self._TakeProfitPercent / 100)
            if price > take_profit_price:
                self.coverLong(-1, position[0].Amount)
                self.calculateProfit(ticker)
                self._TrendWhenTakeProfitOrStopLoss = 1
                self._HadTakeProfit = True
                Log("多单止盈。止盈价格:", _N(take_profit_price, 6), ", 持仓价格:", _N(position[0].Price), self._EnableMessageSend and "@" or "#FF1CAE")
        elif position[0].Type == PD_SHORT:
            take_profit_price = position[0].Price * (1 - self._TakeProfitPercent / 100)
            if price < take_profit_price:
                self.coverShort(-1, position[0].Amount)
                self.calculateProfit(ticker)
                self._TrendWhenTakeProfitOrStopLoss = -1
                self._HadTakeProfit = True
                Log("空单止盈。止盈价格:", _N(take_profit_price, 6), ", 持仓价格:", _N(position[0].Price), self._EnableMessageSend and "@" or "#FF1CAE")

# 回调止盈
def trackingTakeProfit(self, position, ticker):
    take_profit_price = 0
    trigger_price = 0
    price = ticker.Last

    if len(position) > 0 and self._UseTrackingTakeProfit:
        if position[0].Type == PD_LONG:
            # 多单持仓
            if self._TriggeredTakeProfit:
                # 已达到触发价格,监控是否止盈
                self._PeakPriceInPosition = price if price > self._PeakPriceInPosition else self._PeakPriceInPosition  # 更新价格高点
                if self._UsePositionRetracement:
                    take_profit_price = self._PeakPriceInPosition - (self._PeakPriceInPosition - position[0].Price) * (self._CallBakcPercent / 100)  # 计算回调的止盈价格
                else:
                    take_profit_price = self._PeakPriceInPosition * (1 - self._CallBakcPercent / 100)  # 计算回调的止盈价格
                if price < take_profit_price:
                    self.coverLong(-1, position[0].Amount)  # 平多
                    self.calculateProfit(ticker)
                    self._TriggeredTakeProfit = False  # 复位触发标记
                    self._TrendWhenTakeProfitOrStopLoss = 1  # 记录止盈时候的趋势
                    self._HadTakeProfit = True  # 记录发生了止盈
                    Log("多单回调止盈:持仓中价格高点:", _N(self._PeakPriceInPosition, 6), ", 止盈价格:", _N(take_profit_price, 6), ", 当前价格:", _N(price, 6),
                        ", 持仓价格:", _N(position[0].Price, 6), self._EnableMessageSend and "@" or "#FF1CAE")
            else:
                # 监控是否达到回调止盈的触发价格
                trigger_price = position[0].Price * (1 + self._TakeProfitTriggerPercent / 100)
                if price > trigger_price:
                    self._TriggeredTakeProfit = True  # 触发回调止盈
                    self._PeakPriceInPosition = price  # 记录价格高点
                    Log("多单已达到回调止盈的触发价格:", _N(trigger_price, 6), ", 当前价格:", _N(price, 6), ", 持仓价格:", _N(position[0].Price, 6))
        elif position[0].Type == PD_SHORT:
            # 空单持仓
            if self._TriggeredTakeProfit:
                # 已达到触发价格,监控是否止盈
                self._PeakPriceInPosition = price if price < self._PeakPriceInPosition else self._PeakPriceInPosition  # 更新价格低点
                if self._UsePositionRetracement:
                    take_profit_price = self._PeakPriceInPosition + (position[0].Price - self._PeakPriceInPosition) * (self._CallBakcPercent / 100)  # 计算回调的止盈价格
                else:
                    take_profit_price = self._PeakPriceInPosition * (1 + self._CallBakcPercent / 100)  # 计算回调的止盈价格
                if price > take_profit_price:
                    self.coverShort(-1, position[0].Amount)  # 平空
                    self.calculateProfit(ticker)
                    self._TriggeredTakeProfit = False  # 复位触发标记
                    self._TrendWhenTakeProfitOrStopLoss = -1  # 记录止盈时候的趋势
                    self._HadTakeProfit = True  # 记录发生了止盈
                    Log("空单回调止盈:持仓中价格低点:", _N(self._PeakPriceInPosition, 6), ", 止盈价格:", _N(take_profit_price, 6), ", 当前价格:", _N(price, 6),
                        ", 持仓价格:", _N(position[0].Price, 6), self._EnableMessageSend and "@" or "#FF1CAE")
            else:
                # 监控是否达到回调止盈的触发价格
                trigger_price = position[0].Price * (1 - self._TakeProfitTriggerPercent / 100)
                if price < trigger_price:
                    self._TriggeredTakeProfit = True  # 触发回调止盈
                    self._PeakPriceInPosition = price  # 记录价格低点
                    Log("空单已达到回调止盈的触发价格:", _N(trigger_price, 6), ", 当前价格:", _N(price, 6), ", 持仓价格:", _N(position[0].Price, 6))

# 下单
def order(self, long, short, position, ticker):
    position_size = position[0].Amount if len(position) > 0 else 0
    position_type = position[0].Type if len(position) > 0 else None

    if long:
        # 趋势多
        if (self._HadStopLoss or self._HadTakeProfit) and self._TrendWhenTakeProfitOrStopLoss == 1:
            # 发生了止盈止损,并且止盈止损时候趋势为多,不再做多
            return
        if position_size > 0 and position_type == PD_SHORT:
            self.coverShort(-1, position_size)
            self.calculateProfit(ticker)
        elif position_size > 0 and position_type == PD_LONG:
            # 多单持仓,不重复下单
            return
        else:
            # 没有持仓,如果是首次运行或者策略重启,需要等待价格穿过一次EMA均线才下单
            if self._PriceCrossEMAStatus != 2:
                return
        if self.isEnoughAssetToOrder(self._OrderSize, ticker):
            self.openLong(-1, self._OrderSize)
            self._HadStopLoss = False
            self._HadTakeProfit = False
        else:
            raise Exception("下单金额数量不足!")
    elif short:
        # 趋势空

        if (self._HadStopLoss or self._HadTakeProfit) and self._TrendWhenTakeProfitOrStopLoss == -1:
            # 发生了止盈止损,并且止盈止损时候趋势为空,不再做空
            return

        if position_size > 0 and position_type == PD_LONG:
            self.coverLong(-1, position_size)
            self.calculateProfit(ticker)
        elif position_size > 0 and position_type == PD_SHORT:
            # 空单持仓,不重复下单
            return
        else:
            # 没有持仓,如果是首次运行或者策略重启,需要等待价格穿过一次EMA均线才下单
            if self._PriceCrossEMAStatus != 2:
                return

        if self.isEnoughAssetToOrder(self._OrderSize, ticker):
            self.openShort(-1, self._OrderSize)
            self._HadStopLoss = False
            self._HadTakeProfit = False
        else:
            raise Exception("下单金额数量不足!")

# 趋势策略
def trendStrategy(self):
    ticker = _C(exchange.GetTicker)
    position = _C(exchange.GetPosition)
    account = _C(exchange.GetAccount)
    records = _C(exchange.GetRecords, self._KLinePeriod * 60)
    if len(position) > 1:
        Log(position)
        raise Exception("同时有多空持仓!")
    # 策略交互
    self.runCmd()
    # 状态栏信息打印
    self.printLogStatus(ticker, account, position)
    # 止损
    self.stopLoss(position, ticker)
    # 止盈
    self.takeProfit(position, ticker)
    # 回调止盈
    self.trackingTakeProfit(position, ticker)

    # 按照K线周期运行策略
    if not self.runInKLinePeriod(records):
        return
    # 趋势判断和下单
    long = False
    short = False
    [long, short] = self.trendJudgment(records)
    if not self._OnlyTrendJudgment:
        self.order(long, short, position, ticker)

# 状态栏信息打印
def printLogStatus(self, ticker, account, position):
    table_overview = {
        "type": "table",
        "title": "策略总览",
        "cols": ["开始时间", "已运行天数", "交易次数", "胜率", "预估月化%", "预估年化%"],
        "rows": []
    }
    table_account = {
        "type": "table",
        "title": "账户资金",
        "cols": ["当前资产", "初始资产", "可用余额", "冻结余额", "可下单张数", "收益", "收益%"],
        "rows": []
    }
    table_position = {
        "type": "table",
        "title": "持仓情况",
        "cols": ["交易币种", "杠杆倍数", "持仓均价", "方向", "数量", "保证金", "预估强平价格", "浮动盈亏", "浮动盈亏%"],
        "rows": []
    }
    i = 0

    # 策略总览
    the_running_days = self.getDaysFromTimeStamp(self._StrategyDatas["start_run_timestamp"], Unix())
    monthly_rate_of_profit = 0
    if the_running_days > 1:
        monthly_rate_of_profit = self._ProfitLocal / self._InitAsset / the_running_days * 30
    table_overview["rows"].append([_D(self._StrategyDatas["start_run_timestamp"]), the_running_days, self._TradeCount,
                                   0 if self._TradeCount == 0 else (str(_N(self._TakeProfitCount / self._TradeCount * 100, 2)) + "%"),
                                   str(_N(monthly_rate_of_profit * 100, 2)) + "%", str(_N(monthly_rate_of_profit * 12 * 100, 2)) + "%"])
    # 账户资金
    current_asset = self.getAccountAsset(position, account, ticker)
    max_order_size = self.getMaxOrderSize(self._MarginLevel, ticker, account)
    asset_profit = current_asset - self._InitAsset
    asset_profit_percent = asset_profit / self._InitAsset
    table_account["rows"].append([_N(current_asset, 4), _N(self._InitAsset, 4), _N(account.Balance if self._IsUsdtStandard else account.Stocks, 4),
                                  _N(account.FrozenBalance if self._IsUsdtStandard else account.FrozenStocks, 4), max_order_size, _N(asset_profit, 4),
                                  str(_N(asset_profit_percent * 100, 2)) + "%"])
    # 持仓情况
    position_direction = ""
    forced_cover_up_price = 0
    position_profit_percent = 0
    position_profit = 0
    position_margin = 0
    if len(position) == 0:
        table_position["rows"].append(["无持仓", "-", "-", "-", "-", "-", "-", "-", "-"])
    else:
        position_direction = "多单" if position[0].Type == PD_LONG else "空单"
        [position_profit, position_profit_percent] = self.getSinglePositionProfit(position, ticker)
        position_margin = self.getSinglePositionMargin(position, ticker)
        forced_cover_up_price = self.calculateForcedPrice(account, position, ticker)
        table_position["rows"].append([exchange.GetCurrency(), self._MarginLevel, _N(position[0].Price, 4), position_direction, position[0].Amount,
                                       _N(position_margin, 4), _N(forced_cover_up_price, 4), _N(position_profit, 4), s