前回の記事で,私たちは 30 行のコードを簡素化する戦略を学びました. この記事では,私たちは,数値化初心者を数値化戦略のデザインの楽しさに一歩一歩近づかせます. 金融,投資,証券などの分野では全く無知で,先物取引のプロセスも理解できず,BTCの現金取引の仕組みも理解できない. 目の光が乱れ,未知の名前や用語が耳にされ,頭がぼんやりする. (その実現は,少しでも理解できる!少しでも理解できる!) │自分の百度,資料などで一回ずつ頭が補足される。 関連コンテンツを閲覧し,基本的な概念を心に留めて,自分の少ししか知らないJS言語と組み合わせて,簡潔に書きました.最初はK線,平均線,MACD指標が何なのかわかりませんでした. ここで簡単に言えば,K線は一定の周期内の市場動向を記録し,市場動態を観察するのに便利である.均線は,前回の記事で使用された指標であり,MACD指標と同様に,市場動向を反映する指標である. この2つの指標の概念,アルゴリズム,公式推論などについては,それぞれ説明しています. わからない方は百度を見てください. (私は百度を見ている!)
| 変数の名前 | 初期値 | 例示する |
|---|---|---|
| Interval | 2000 | この変数はアンケート周期,つまり,プログラムが一時停止を待つ時間の量で,単位はミリ秒,1000ミリ秒は1秒,したがってこの変数の初期値は2秒である. |
| STATE_FREE | 0 | これは状態を表す変数で,空を表す. 状態判断のために用いられる. |
| STATE_BUY | 1 | これは,多頭保有の状態を表す変数です. |
| STATE_SELL | 2 | 状態変数,空頭保有を表示する. |
| ORDER_INVALID | 3 | 持仓状態の変数,持仓していないことを示す. |
| ORDER_VALID | 4 | 持ってるってこと? |
| state | STATE_FREE | 状態変数,空の状態で初期化. |
| SignalDelay | 0 | 信号が遅れており,一時的に無効です. |
| stopProfit | 0.002 | この変数は重要で,ストップローズは,例えば本金 * ストップローズ ((0.002) は,本金の0.002倍の最大損失を表し,損失の上限である. |
| step | 0.5 | スライドストップのステップ長さ. ストップ価格の上昇,下降の等級. |
| opAmount | 1 | 固定された操作量 |
| profit | 0 | 失敗する |
var holdOrder = {//持仓信息对象
orderState: ORDER_INVALID,// 持仓状态
price: 0, //持仓均价
amount: 0, //持仓量
time: null, // 操作时间
stopPrice: 0, // 止损价
level: 1, //止损等级
updateCurrentProfit: function(lastPrice,amount){//更新当前盈亏
if(state === STATE_SELL){//当前 空头持仓
return (lastPrice - this.price) * amount;
}
if(state === STATE_BUY){//当前 多头持仓
return - (lastPrice - this.price) * amount;
}
},
SetStopPrice: function(ticker,stopState){//更新止损价
if(stopState === STATE_FREE){ //更新止损时状态 为空闲
return this.stopPrice;
}
if(stopState === STATE_BUY){ //更新止损时状态 为多仓
if(this.orderState === ORDER_INVALID){
return this.stopPrice;
}
if(this.stopPrice === 0){//初始 止损价为0 时
this.stopPrice = this.price * ( 1 - stopProfit );
}
if( ticker.Last <= this.price ){ //最后成交价 小于等于 持仓均价时
this.stopPrice = this.price * ( 1 - stopProfit );
this.level = 1;
}else{//其它情况
if( ticker.Last - this.price > this.level * step ){//超出当前等级 设置滑动止损
this.stopPrice = this.price * (1 - stopProfit) + (ticker.Last - this.price );
//更新止损价为滑动后的止损价
this.level++;//上调止损等级
}else{//其它
this.stopPrice = this.stopPrice;//保持当前止损价不变
}
}
}else if( stopState === STATE_SELL){//空头持仓类似
if(this.orderState === ORDER_INVALID){
return this.stopPrice;
}
if(this.stopPrice === 0){
this.stopPrice = this.price * ( 1 + stopProfit );
}
if( ticker.Last >= this.price ){
this.stopPrice = this.price * ( 1 + stopProfit );
this.level = 1;
}else{
if( this.price - ticker.Last > this.level * step ){
this.stopPrice = this.price * (1 + stopProfit) - ( this.price - ticker.Last );
this.level++;
}else{
this.stopPrice = this.stopPrice;
}
}
}
return this.stopPrice;//返回止损价
},
initHoldOrder: function(){//平仓后 用于 初始化持仓信息的 函数
this.orderState = ORDER_INVALID;
this.price = 0;
this.amount = 0;
this.time = null;
this.stopPrice = 0;
this.level = 1;
}
};
コードをgithubにアップロードする クリックgithub入力しました
公式QQグループに参加していない方は, 309368835の発明者量化交流グループにご参加ください.
function MACD_Cross(){//检测MACD指标,交叉状态的函数
var records = exchange.GetRecords();//获取K线数据
while(!records || records.length < 45){ //K线数据不能为null,要大于45个柱,不符合标准 循环获取直到符合
records = exchange.GetRecords();
Sleep(Interval);
}
var macd = TA.MACD(records,12,26,9);//调用指标函数, 参数为MACD 默认的参数。
var dif = macd[0]; //dif线
var dea = macd[1]; //dea线
var column = macd[2]; // MACD柱
var len = records.length; //K线周期长度
if( (dif[len-1] > 0 && dea[len-1] > 0) && dif[len-1] > dea[len-1] && dif[len-2] < dea[len-2] && column[len-1] > 0.2 ){
//判断金叉条件:dif 与 dea 此刻均大于0 , 且dif由下上穿dea , 且 MACD量柱大于0.2
return 1; //返回1 代表 金叉信号。
}
if( (dif[len-1] < 0 && dea[len-1] < 0) && dif[len-1] < dea[len-1] && dif[len-2] > dea[len-2] && column[len-1] < -0.2 ){
//判断死叉条件:
return 2;//返回2 代表 死叉信号。
}
return 0; //金叉 、死叉 信号以外,为等待信号 0 。
}
function getTimeByNormal(time){// 获取时间的 函数 把毫秒时间 转换 标准时间
var timeByNormal = new Date();
timeByNormal.setTime(time);
var strTime = timeByNormal.toString();
var showTimeArr = strTime.split(" ");
var showTime = showTimeArr[3]+"-"+showTimeArr[1]+"-"+showTimeArr[2]+"-"+showTimeArr[4];
return showTime;
}
function main(){
var initAccount = $.GetAccount(exchange);//首先我们来记录初始时的账户信息,这里调用了模板类库的导出函数
var nowAccount = initAccount;//再声明一个 变量 表示 现在账户信息
var diffMoney = 0; //钱 差额
var diffStocks = 0;//币 差额
var repair = 0; //计算 盈亏时 用于修正的 量
var ticker = exchange.GetTicker(); //获取此刻市场行情
Log("初始账户:",initAccount); //输出显示 初始账户信息。
while(true){//主函数循环
scan(); //扫描函数, 稍后讲解,主要是判断 开仓、平仓 以及 操作 开仓 、 平仓。
ticker = exchange.GetTicker();//在while循环内 获取 市场行情
if(!ticker){//如果 没有获取到 (null) 跳过以下 重新循环
continue;
}
if(holdOrder.orderState == ORDER_VALID){//判断当前是否 持仓
Log("当前持仓:",holdOrder); //如果 当前持仓 输出 持仓 信息
}
if(holdOrder.orderState == ORDER_INVALID){//如果 未持仓(已平仓)
nowAccount = $.GetAccount(exchange); //获取当前账户信息
diffMoney = nowAccount.Balance - initAccount.Balance; //计算 当前账户 与 初始账户之间的 钱 差额
diffStocks = nowAccount.Stocks - initAccount.Stocks; // 计算 当前账户 与 初始账户之间的 币 差额
repair = diffStocks * ticker.Last; //把 币的差额 * 最后成交价 ,转为等值的钱, 用于计算 盈亏
LogProfit(diffMoney + repair ,"RMB","现在账户:",nowAccount,"本次盈亏:",profit);//输出 盈亏 信息
}
Sleep(Interval);//轮询
}
}
function scan(){
var sellInfo = null; //声明 储存平仓信息的变量 , 初始化null
var buyInfo = null; //声明 开仓的 , 初始化null
var opFun = null;// 开仓函数, 两种状态 , 开多仓 , 开空仓。
var singal = 0; //信号
while(true){//检测 及操作 循环
var ticker = exchange.GetTicker(); //获取市场行情
if(!ticker){ //判断 获取失败 跳过以下 ,继续循环获取
continue;
}
holdOrder.SetStopPrice(ticker,state); //设置 持仓 止损价
if(state === STATE_FREE && (singal = MACD_Cross()) !== 0 ){
//判断策略运行状态是否空闲、此刻MACD指标信号是否空闲, 符合 策略运行状态空闲 且 有金叉或死叉执行以下
holdOrder.initHoldOrder();//初始化持仓信息
opFun = singal === 1 ? $.Buy : $.Sell ;//根据MACD_Cross函数返回结果,判断开多仓、开空仓。
buyInfo = opFun(opAmount);//开仓操作
holdOrder.orderState = ORDER_VALID;//设置持仓信息,状态为持仓
holdOrder.price = buyInfo.price; //设置持仓均价 由 开仓操作函数 opFun返回。
holdOrder.amount = buyInfo.amount; //设置持仓量
holdOrder.time = getTimeByNormal((new Date()).getTime());//设置持仓开始的时间
state = singal === 1 ? STATE_BUY : STATE_SELL; //更新策略状态为多仓 或 空仓
var account = $.GetAccount(exchange); //获取账户信息
if(singal === 1){//输出开仓方向 和 当前账户信息
Log("开多仓。","账户:",account);
}else{
Log("开空仓。","账户:",account);
}
break;
}else{
var lastPrice = holdOrder.price;// 把持仓均价 赋值 给 lastPrice
if( state === STATE_BUY && holdOrder.orderState === ORDER_VALID && ticker.Last < holdOrder.stopPrice ){
//如果 多仓 且 持仓信息为持仓 且 最后成交价 小于止损价,执行以下
Log("多头止损平仓","初始止损价:",holdOrder.price * (1 - stopProfit),"--滑动止损价:",holdOrder.stopPrice,"最后成交价:",ticker.Last,"止损等级:",holdOrder.level);//多头止损平仓信息
sellInfo = $.Sell(holdOrder.amount);//平仓
holdOrder.orderState = ORDER_INVALID;//平仓信息 更新进对象
holdOrder.price = sellInfo.price;
holdOrder.amount = sellInfo.amount;
holdOrder.time = getTimeByNormal((new Date()).getTime());
profit = holdOrder.updateCurrentProfit(lastPrice,sellInfo.amount);//更新浮动盈亏
state = STATE_FREE;//更新状态
break;//跳出
}
if( state === STATE_SELL && holdOrder.orderState === ORDER_VALID && ticker.Last > holdOrder.stopPrice ){//同上 , 这个是空头止损平仓
Log("空头止损平仓","初始止损价:",holdOrder.price * (1 + stopProfit),"--滑动止损价:",holdOrder.stopPrice,"最后成交价:",ticker.Last,"止损等级:",holdOrder.level);//测试
sellInfo = $.Buy(holdOrder.amount);
holdOrder.orderState = ORDER_INVALID;
holdOrder.price = sellInfo.price;
holdOrder.amount = sellInfo.amount;
holdOrder.time = getTimeByNormal((new Date()).getTime());
profit = holdOrder.updateCurrentProfit(lastPrice,sellInfo.amount);
state = STATE_FREE;
break;
}
if(state === STATE_BUY && MACD_Cross() === 2 ){//做多时,MACD指标死叉 -- 死叉平仓
sellInfo = $.Sell(holdOrder.amount);
Log("死叉平仓","初始止损价:",holdOrder.price * (1 - stopProfit),"--滑动止损价:",holdOrder.stopPrice,"最后成交价:",ticker.Last,"止损等级:",holdOrder.level);//测试
holdOrder.orderState = ORDER_INVALID;
holdOrder.price = sellInfo.price;
holdOrder.amount = sellInfo.amount;
holdOrder.time = getTimeByNormal((new Date()).getTime());
profit = holdOrder.updateCurrentProfit(lastPrice,sellInfo.amount);
state = STATE_FREE;
break;
}
if(state === STATE_SELL && MACD_Cross() === 1 ){//做空时,MACD指标金叉 ---金叉平仓
sellInfo = $.Buy(holdOrder.amount);
Log("金叉平仓","初始止损价:",holdOrder.price * (1 + stopProfit),"--滑动止损价:",holdOrder.stopPrice,"最后成交价:",ticker.Last,"止损等级:",holdOrder.level);//测试
holdOrder.orderState = ORDER_INVALID;
holdOrder.price = sellInfo.price;
holdOrder.amount = sellInfo.amount;
holdOrder.time = getTimeByNormal((new Date()).getTime());
profit = holdOrder.updateCurrentProfit(lastPrice,sellInfo.amount);
state = STATE_FREE;
break;
}
}
Sleep(Interval);//轮询间隔,就是让程序暂停一会儿。
}
}
スライドストップの原理についてです
このスライドストップに関するコードには,SetStopPriceこの関数は,stopState(止損状態) とticker(市場データ) ストップ・ロスを更新する.stopState === STATE_BUY異なる状況に応じて判断し,ストップ価格を更新します.orderState無効状態 ((つまり有効なポジションを所持していない) として,現在のストップ・コストを返します. ストップ・コストが0である場合は,それを初期化して購入平均価格に掛けます.(1 - stopProfit)│ │ │ │ │ │ │ │ticker.Last) と持仓平均価格 ((this.price) の差値と現在のストップグレード ((this.level) とステップ長さの倍数と比較する.現在のレベルを超えた場合,ストップ・プローストをスライド後の値に更新し,ストップ・プローストを増加させる.そうでない場合は,現在のストップ・プローストを保持する.空頭ポジションについては,stopState === STATE_SELL),論理は同様だが,最終取引価格と保有均価の差を負の値として取り,更新したストップ・ロスの価格でその差を減算する.最後に,更新後のストップ・ロスの価格を返す.
スライド・ストップはリスク管理策です.
ポジションを保有する過程で,市場価格の変動に応じてストップ・ロスを調整して損失を減らすか,利益を保護する.コードの論理によれば,以下のキーポイントでスライド・ストップを実現することが見られます.updateCurrentProfit方法は,現在の利益と損失を更新するために,保有状態 ((state) と最新の価格 ((lastPrice) によって現在の利益を計算します. 保有状態が空売り状態 ((STATE_SELL) であれば,損失は,保有価格に保有価格の差を掛けます. 保有状態が多頭状態 ((STATE_BUY) であれば,損失は負になります. SetStopPrice 方法は,停止価格を更新するために使用される.1 - stopProfit),およびストップレベルを1にリセットする. 最終取引価格が現在のレベルを超えたステップを[[ステップ]]にすると,ストップ価格をスライド後のストップ価格に設定し,ストップレベルをアップする. その他の場合,ストップ価格を不変に保つ. ストップ状態が空頭[[STATE_SELL]]である場合,論理は類似する. initHoldOrder方法は,平仓後に保有情報を初期化するために使用され,保有状態,平均,数量,時間操作価格,ストップ価格,およびストップレベルを初期状態にリセットする.
参考資料