Azure 数据资源管理器 (Kusto/KQL) 金融资产回测跟踪停止
Azure Data Explorer (Kusto/KQL) Financial Asset Backtesting Trail Stop
我有一个金融资产价格随时间变化的数据集,我想模拟跟踪止损以针对该数据集进行回溯测试策略。
追踪止损是一些在线经纪商支持的一种交易订单类型,用于开仓时的止损或利润保护,设置追踪止损以在满足价格条件时自动止损。
跟踪止损单会随着资产价格的上涨而增加,并在开仓期间保持在最大值,一旦资产价格跌破跟踪止损最大值,经纪商将平仓.
在这种情况下,跟踪止损是资产价格的百分比。即资产价格减去 3%。
我尝试了多种方法,包括汇总和扫描运算符,但似乎无法找到工作原型。
下面是价格随时间变化的资产的示例数据 table。
//Trail Stop Properties:
//Trail stop will follow an asset price as it increases
// and remain at the max of the asset price increase during an open position
// the position will be closed when the price is less than
// or equal to the trail stop value.
//Usually the stop is set with a percentage of loss from the trailing price.
//i.e. in the below example the trailing stop is 0.03 or 3% of the asset price.
let trailstop = double(0.03);
let assets = datatable
(
Timestamp:datetime, Symbol:string, StrikePrice:double, CallPremium:double,
PositionId:int
)
[
datetime(2022-03-16T13:57:55.815Z), 'SPY' ,432, 2.46, 1,
datetime(2022-03-16T14:00:55.698Z), 'SPY' ,432, 2.48, 1,
datetime(2022-03-16T14:01:15.876Z), 'SPY' ,432, 2.49, 1,
datetime(2022-03-16T14:08:25.536Z), 'SPY' ,431, 2.45, 1,
datetime(2022-03-16T14:18:25.675Z), 'SPY' ,434, 2.40, 1,
datetime(2022-03-16T14:21:50.887Z), 'SPY' ,434, 2.40, 2,
datetime(2022-03-16T14:35:00.835Z), 'SPY' ,434, 2.33, 2
]
;
assets
| sort by Timestamp asc
| extend TrailStop = round(CallPremium - (CallPremium * trailstop),2)
| extend rn = row_number()
输出
2022-03-16T13:57:55.815Z SPY 432 2.46 1 2.39 1
2022-03-16T14:00:55.698Z SPY 432 2.48 1 2.41 2
2022-03-16T14:01:15.876Z SPY 432 2.49 1 2.42 3
2022-03-16T14:08:25.536Z SPY 431 2.45 1 2.38 4
2022-03-16T14:18:25.675Z SPY 434 2.4 1 2.33 5
2022-03-16T14:21:50.887Z SPY 434 2.4 2 2.33 6
2022-03-16T14:35:00.835Z SPY 434 2.33 2 2.26 7
如果追踪止损正常工作,并且有开仓和平仓列指示追踪止损发生的时间,导致平仓,结果集将类似于以下数据的输出table .
let outcomes = datatable
(
Timestamp:datetime, Symbol:string, StrikePrice:double, CallPremium:double,
PositionId:int, TrailStop:double, PositionOpen:int, PositionClose:int
)
[
datetime(2022-03-16T13:57:55.815Z), 'SPY', 432, 2.46, 1, 2.39, 1, 0,
datetime(2022-03-16T14:00:55.698Z), 'SPY', 432, 2.48, 1, 2.41, 1, 0,
datetime(2022-03-16T14:01:15.876Z), 'SPY', 432, 2.49, 1, 2.42, 1, 0,
datetime(2022-03-16T14:08:25.536Z), 'SPY', 431, 2.45, 1, 2.42, 1, 0,
datetime(2022-03-16T14:18:25.675Z), 'SPY', 434, 2.40, 1, 2.42, 0, 1,
datetime(2022-03-16T14:21:50.887Z), 'SPY', 434, 2.40, 2, 2.33, 1, 0,
datetime(2022-03-16T14:35:00.835Z), 'SPY', 434, 2.33, 2, 2.26, 0, 1
]
;
outcomes
| sort by Timestamp asc
| extend rn = row_number()
输出
2022-03-16T13:57:55.815Z SPY 432 2.46 1 2.39 1 0 1
2022-03-16T14:00:55.698Z SPY 432 2.48 1 2.41 1 0 2
2022-03-16T14:01:15.876Z SPY 432 2.49 1 2.42 1 0 3
2022-03-16T14:08:25.536Z SPY 431 2.45 1 2.42 1 0 4
2022-03-16T14:18:25.675Z SPY 434 2.4 1 2.42 0 1 5
2022-03-16T14:21:50.887Z SPY 434 2.4 2 2.33 1 0 6
2022-03-16T14:35:00.835Z SPY 434 2.33 2 2.26 0 1 7
最终结果将是两个开仓和平仓。
位置 1
在 2.46 开盘 (rn=1) 并在 2.42
收盘 (rn=5)
仓位 2 开盘价 (rn=6) 2.40 平仓 (rn=7) 2.33
如有任何帮助、想法或指导,我们将不胜感激。
Investopedia 关于追踪止损的很好解释
- 按位置和时间戳排序
- 使用 prev() 确定新位置的起点。
- 使用 scan() 计算 运行 CallPremium 最大值(总是上升,为新位置重置)。
- 将每个 CallPremium 与 运行 最大值进行比较,并检查是否达到追踪止损。
let trailstop = double(0.03);
let assets = datatable
(
Timestamp:datetime, Symbol:string, StrikePrice:double, CallPremium:double,
PositionId:int
)
[
datetime(2022-03-16T13:57:55.815Z), 'SPY' ,432, 2.46, 1,
datetime(2022-03-16T14:00:55.698Z), 'SPY' ,432, 2.48, 1,
datetime(2022-03-16T14:01:15.876Z), 'SPY' ,432, 2.49, 1,
datetime(2022-03-16T14:08:25.536Z), 'SPY' ,431, 2.45, 1,
datetime(2022-03-16T14:18:25.675Z), 'SPY' ,434, 2.40, 1,
datetime(2022-03-16T14:21:50.887Z), 'SPY' ,434, 2.40, 2,
datetime(2022-03-16T14:35:00.835Z), 'SPY' ,434, 2.33, 2
]
;
assets
| sort by PositionId asc, Timestamp asc
| extend PositionId_start = prev(PositionId) != PositionId
| scan declare (CallPremium_running_max:double = double(null))
with
(
step s1 : true => CallPremium_running_max =
max_of(iff(PositionId_start,double(null),s1.CallPremium_running_max),CallPremium);
)
| extend TrailStop = round(CallPremium_running_max*(1-trailstop),2)
| extend PositionOpen = iff(CallPremium <= TrailStop,1,0)
| extend PositionClose = 1 - PositionOpen
Timestamp
Symbol
StrikePrice
CallPremium
PositionId
PositionId_start
CallPremium_running_max
TrailStop
PositionOpen
PositionClose
2022-03-16T13:57:55.815Z
SPY
432
2.46
1
true
2.46
2.39
0
1
2022-03-16T14:00:55.698Z
SPY
432
2.48
1
false
2.48
2.41
0
1
2022-03-16T14:01:15.876Z
SPY
432
2.49
1
false
2.49
2.42
0
1
2022-03-16T14:08:25.536Z
SPY
431
2.45
1
false
2.49
2.42
0
1
2022-03-16T14:18:25.675Z
SPY
434
2.4
1
false
2.49
2.42
1
0
2022-03-16T14:21:50.887Z
SPY
434
2.4
2
true
2.4
2.33
0
1
2022-03-16T14:35:00.835Z
SPY
434
2.33
2
false
2.4
2.33
1
0
我有一个金融资产价格随时间变化的数据集,我想模拟跟踪止损以针对该数据集进行回溯测试策略。
追踪止损是一些在线经纪商支持的一种交易订单类型,用于开仓时的止损或利润保护,设置追踪止损以在满足价格条件时自动止损。
跟踪止损单会随着资产价格的上涨而增加,并在开仓期间保持在最大值,一旦资产价格跌破跟踪止损最大值,经纪商将平仓.
在这种情况下,跟踪止损是资产价格的百分比。即资产价格减去 3%。
我尝试了多种方法,包括汇总和扫描运算符,但似乎无法找到工作原型。
下面是价格随时间变化的资产的示例数据 table。
//Trail Stop Properties:
//Trail stop will follow an asset price as it increases
// and remain at the max of the asset price increase during an open position
// the position will be closed when the price is less than
// or equal to the trail stop value.
//Usually the stop is set with a percentage of loss from the trailing price.
//i.e. in the below example the trailing stop is 0.03 or 3% of the asset price.
let trailstop = double(0.03);
let assets = datatable
(
Timestamp:datetime, Symbol:string, StrikePrice:double, CallPremium:double,
PositionId:int
)
[
datetime(2022-03-16T13:57:55.815Z), 'SPY' ,432, 2.46, 1,
datetime(2022-03-16T14:00:55.698Z), 'SPY' ,432, 2.48, 1,
datetime(2022-03-16T14:01:15.876Z), 'SPY' ,432, 2.49, 1,
datetime(2022-03-16T14:08:25.536Z), 'SPY' ,431, 2.45, 1,
datetime(2022-03-16T14:18:25.675Z), 'SPY' ,434, 2.40, 1,
datetime(2022-03-16T14:21:50.887Z), 'SPY' ,434, 2.40, 2,
datetime(2022-03-16T14:35:00.835Z), 'SPY' ,434, 2.33, 2
]
;
assets
| sort by Timestamp asc
| extend TrailStop = round(CallPremium - (CallPremium * trailstop),2)
| extend rn = row_number()
输出
2022-03-16T13:57:55.815Z SPY 432 2.46 1 2.39 1
2022-03-16T14:00:55.698Z SPY 432 2.48 1 2.41 2
2022-03-16T14:01:15.876Z SPY 432 2.49 1 2.42 3
2022-03-16T14:08:25.536Z SPY 431 2.45 1 2.38 4
2022-03-16T14:18:25.675Z SPY 434 2.4 1 2.33 5
2022-03-16T14:21:50.887Z SPY 434 2.4 2 2.33 6
2022-03-16T14:35:00.835Z SPY 434 2.33 2 2.26 7
如果追踪止损正常工作,并且有开仓和平仓列指示追踪止损发生的时间,导致平仓,结果集将类似于以下数据的输出table .
let outcomes = datatable
(
Timestamp:datetime, Symbol:string, StrikePrice:double, CallPremium:double,
PositionId:int, TrailStop:double, PositionOpen:int, PositionClose:int
)
[
datetime(2022-03-16T13:57:55.815Z), 'SPY', 432, 2.46, 1, 2.39, 1, 0,
datetime(2022-03-16T14:00:55.698Z), 'SPY', 432, 2.48, 1, 2.41, 1, 0,
datetime(2022-03-16T14:01:15.876Z), 'SPY', 432, 2.49, 1, 2.42, 1, 0,
datetime(2022-03-16T14:08:25.536Z), 'SPY', 431, 2.45, 1, 2.42, 1, 0,
datetime(2022-03-16T14:18:25.675Z), 'SPY', 434, 2.40, 1, 2.42, 0, 1,
datetime(2022-03-16T14:21:50.887Z), 'SPY', 434, 2.40, 2, 2.33, 1, 0,
datetime(2022-03-16T14:35:00.835Z), 'SPY', 434, 2.33, 2, 2.26, 0, 1
]
;
outcomes
| sort by Timestamp asc
| extend rn = row_number()
输出
2022-03-16T13:57:55.815Z SPY 432 2.46 1 2.39 1 0 1
2022-03-16T14:00:55.698Z SPY 432 2.48 1 2.41 1 0 2
2022-03-16T14:01:15.876Z SPY 432 2.49 1 2.42 1 0 3
2022-03-16T14:08:25.536Z SPY 431 2.45 1 2.42 1 0 4
2022-03-16T14:18:25.675Z SPY 434 2.4 1 2.42 0 1 5
2022-03-16T14:21:50.887Z SPY 434 2.4 2 2.33 1 0 6
2022-03-16T14:35:00.835Z SPY 434 2.33 2 2.26 0 1 7
最终结果将是两个开仓和平仓。
位置 1 在 2.46 开盘 (rn=1) 并在 2.42
收盘 (rn=5)仓位 2 开盘价 (rn=6) 2.40 平仓 (rn=7) 2.33
如有任何帮助、想法或指导,我们将不胜感激。
Investopedia 关于追踪止损的很好解释
- 按位置和时间戳排序
- 使用 prev() 确定新位置的起点。
- 使用 scan() 计算 运行 CallPremium 最大值(总是上升,为新位置重置)。
- 将每个 CallPremium 与 运行 最大值进行比较,并检查是否达到追踪止损。
let trailstop = double(0.03);
let assets = datatable
(
Timestamp:datetime, Symbol:string, StrikePrice:double, CallPremium:double,
PositionId:int
)
[
datetime(2022-03-16T13:57:55.815Z), 'SPY' ,432, 2.46, 1,
datetime(2022-03-16T14:00:55.698Z), 'SPY' ,432, 2.48, 1,
datetime(2022-03-16T14:01:15.876Z), 'SPY' ,432, 2.49, 1,
datetime(2022-03-16T14:08:25.536Z), 'SPY' ,431, 2.45, 1,
datetime(2022-03-16T14:18:25.675Z), 'SPY' ,434, 2.40, 1,
datetime(2022-03-16T14:21:50.887Z), 'SPY' ,434, 2.40, 2,
datetime(2022-03-16T14:35:00.835Z), 'SPY' ,434, 2.33, 2
]
;
assets
| sort by PositionId asc, Timestamp asc
| extend PositionId_start = prev(PositionId) != PositionId
| scan declare (CallPremium_running_max:double = double(null))
with
(
step s1 : true => CallPremium_running_max =
max_of(iff(PositionId_start,double(null),s1.CallPremium_running_max),CallPremium);
)
| extend TrailStop = round(CallPremium_running_max*(1-trailstop),2)
| extend PositionOpen = iff(CallPremium <= TrailStop,1,0)
| extend PositionClose = 1 - PositionOpen
Timestamp | Symbol | StrikePrice | CallPremium | PositionId | PositionId_start | CallPremium_running_max | TrailStop | PositionOpen | PositionClose |
---|---|---|---|---|---|---|---|---|---|
2022-03-16T13:57:55.815Z | SPY | 432 | 2.46 | 1 | true | 2.46 | 2.39 | 0 | 1 |
2022-03-16T14:00:55.698Z | SPY | 432 | 2.48 | 1 | false | 2.48 | 2.41 | 0 | 1 |
2022-03-16T14:01:15.876Z | SPY | 432 | 2.49 | 1 | false | 2.49 | 2.42 | 0 | 1 |
2022-03-16T14:08:25.536Z | SPY | 431 | 2.45 | 1 | false | 2.49 | 2.42 | 0 | 1 |
2022-03-16T14:18:25.675Z | SPY | 434 | 2.4 | 1 | false | 2.49 | 2.42 | 1 | 0 |
2022-03-16T14:21:50.887Z | SPY | 434 | 2.4 | 2 | true | 2.4 | 2.33 | 0 | 1 |
2022-03-16T14:35:00.835Z | SPY | 434 | 2.33 | 2 | false | 2.4 | 2.33 | 1 | 0 |